2025-10-27 23:12:15 +08:00
|
|
|
|
package config
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"bytes"
|
|
|
|
|
|
"encoding/json"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"io/fs"
|
|
|
|
|
|
"os"
|
2025-10-31 09:38:36 +08:00
|
|
|
|
"path/filepath"
|
2025-10-27 23:12:15 +08:00
|
|
|
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// 结构体定义
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
// ServerConfig 服务器配置结构体
|
|
|
|
|
|
// 包含服务器运行相关的配置信息
|
|
|
|
|
|
type ServerConfig struct {
|
|
|
|
|
|
Host string `json:"host" mapstructure:"host"` // 服务器监听地址
|
|
|
|
|
|
Port int `json:"port" mapstructure:"port"` // 服务器监听端口
|
|
|
|
|
|
Dist string `json:"dist" mapstructure:"dist"` // 静态文件目录
|
|
|
|
|
|
DevMode bool `json:"dev_mode" mapstructure:"dev_mode"` // 开发模式(跳过验证码等)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DatabaseConfig 数据库配置结构体
|
|
|
|
|
|
// 包含数据库连接相关的配置信息
|
|
|
|
|
|
type DatabaseConfig struct {
|
|
|
|
|
|
Type string `json:"type" mapstructure:"type"` // 数据库类型(mysql/sqlite)
|
|
|
|
|
|
MySQL MySQLConfig `json:"mysql" mapstructure:"mysql"` // MySQL配置
|
|
|
|
|
|
SQLite SQLiteConfig `json:"sqlite" mapstructure:"sqlite"` // SQLite配置
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MySQLConfig MySQL数据库配置结构体
|
|
|
|
|
|
// 包含MySQL数据库连接的详细配置信息
|
|
|
|
|
|
type MySQLConfig struct {
|
|
|
|
|
|
Host string `json:"host" mapstructure:"host"` // 数据库主机地址
|
|
|
|
|
|
Port int `json:"port" mapstructure:"port"` // 数据库端口
|
|
|
|
|
|
Username string `json:"username" mapstructure:"username"` // 数据库用户名
|
|
|
|
|
|
Password string `json:"password" mapstructure:"password"` // 数据库密码
|
|
|
|
|
|
Database string `json:"database" mapstructure:"database"` // 数据库名称
|
|
|
|
|
|
Charset string `json:"charset" mapstructure:"charset"` // 字符集
|
|
|
|
|
|
MaxIdleConns int `json:"max_idle_conns" mapstructure:"max_idle_conns"` // 最大空闲连接数
|
|
|
|
|
|
MaxOpenConns int `json:"max_open_conns" mapstructure:"max_open_conns"` // 最大打开连接数
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SQLiteConfig SQLite数据库配置结构体
|
|
|
|
|
|
// 包含SQLite数据库文件路径配置
|
|
|
|
|
|
type SQLiteConfig struct {
|
|
|
|
|
|
Path string `json:"path" mapstructure:"path"` // 数据库文件路径
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// RedisConfig Redis配置结构体
|
|
|
|
|
|
// 包含Redis连接相关的配置信息
|
|
|
|
|
|
type RedisConfig struct {
|
|
|
|
|
|
Host string `json:"host" mapstructure:"host"` // Redis服务器地址
|
|
|
|
|
|
Port int `json:"port" mapstructure:"port"` // Redis服务器端口
|
|
|
|
|
|
Password string `json:"password" mapstructure:"password"` // Redis密码
|
|
|
|
|
|
DB int `json:"db" mapstructure:"db"` // Redis数据库编号
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// LogConfig 日志配置结构体
|
|
|
|
|
|
// 包含日志记录相关的配置信息
|
|
|
|
|
|
type LogConfig struct {
|
|
|
|
|
|
Level string `json:"level" mapstructure:"level"` // 日志级别
|
|
|
|
|
|
File string `json:"file" mapstructure:"file"` // 日志文件路径
|
|
|
|
|
|
MaxSize int `json:"max_size" mapstructure:"max_size"` // 单个日志文件最大大小(MB)
|
|
|
|
|
|
MaxBackups int `json:"max_backups" mapstructure:"max_backups"` // 保留的旧日志文件数量
|
|
|
|
|
|
MaxAge int `json:"max_age" mapstructure:"max_age"` // 日志文件保留天数
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CookieConfig Cookie配置结构体
|
|
|
|
|
|
// 包含Cookie相关的安全配置信息
|
|
|
|
|
|
type CookieConfig struct {
|
|
|
|
|
|
Secure bool `json:"secure" mapstructure:"secure"` // 是否只在HTTPS下发送Cookie
|
|
|
|
|
|
SameSite string `json:"same_site" mapstructure:"same_site"` // SameSite属性(Strict/Lax/None)
|
|
|
|
|
|
Domain string `json:"domain" mapstructure:"domain"` // Cookie域名
|
|
|
|
|
|
MaxAge int `json:"max_age" mapstructure:"max_age"` // Cookie最大存活时间(秒)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SecurityConfig 安全配置结构体
|
|
|
|
|
|
// 包含应用程序安全相关的配置信息
|
|
|
|
|
|
type SecurityConfig struct {
|
|
|
|
|
|
JWTSecret string `json:"jwt_secret" mapstructure:"jwt_secret"` // JWT签名密钥
|
|
|
|
|
|
EncryptionKey string `json:"encryption_key" mapstructure:"encryption_key"` // 数据加密密钥
|
|
|
|
|
|
JWTRefresh int `json:"jwt_refresh" mapstructure:"jwt_refresh"` // JWT令牌刷新阈值(小时)
|
|
|
|
|
|
Cookie CookieConfig `json:"cookie" mapstructure:"cookie"` // Cookie配置
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// AppConfig 应用配置结构体
|
|
|
|
|
|
type AppConfig struct {
|
|
|
|
|
|
Server ServerConfig `json:"server" mapstructure:"server"`
|
|
|
|
|
|
Database DatabaseConfig `json:"database" mapstructure:"database"`
|
|
|
|
|
|
Redis RedisConfig `json:"redis" mapstructure:"redis"`
|
|
|
|
|
|
Log LogConfig `json:"log" mapstructure:"log"`
|
|
|
|
|
|
Security SecurityConfig `json:"security" mapstructure:"security"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// 公共函数
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
// GetDefaultAppConfig 获取默认应用配置
|
|
|
|
|
|
func GetDefaultAppConfig() *AppConfig {
|
|
|
|
|
|
return &AppConfig{
|
|
|
|
|
|
Server: ServerConfig{
|
|
|
|
|
|
Host: "0.0.0.0",
|
|
|
|
|
|
Port: 8080,
|
|
|
|
|
|
Dist: "",
|
|
|
|
|
|
DevMode: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
Database: DatabaseConfig{
|
|
|
|
|
|
Type: "sqlite",
|
|
|
|
|
|
MySQL: MySQLConfig{
|
|
|
|
|
|
Host: "localhost",
|
|
|
|
|
|
Port: 3306,
|
2025-10-31 09:38:36 +08:00
|
|
|
|
Username: "",
|
|
|
|
|
|
Password: "",
|
|
|
|
|
|
Database: "",
|
2025-10-27 23:12:15 +08:00
|
|
|
|
Charset: "utf8mb4",
|
|
|
|
|
|
MaxIdleConns: 10,
|
|
|
|
|
|
MaxOpenConns: 100,
|
|
|
|
|
|
},
|
|
|
|
|
|
SQLite: SQLiteConfig{
|
|
|
|
|
|
Path: "./database.db",
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
Redis: RedisConfig{
|
|
|
|
|
|
Host: "localhost",
|
|
|
|
|
|
Port: 6379,
|
|
|
|
|
|
Password: "",
|
|
|
|
|
|
DB: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
Log: LogConfig{
|
|
|
|
|
|
Level: "info",
|
|
|
|
|
|
File: "./logs/app.log",
|
|
|
|
|
|
MaxSize: 100,
|
|
|
|
|
|
MaxBackups: 5,
|
|
|
|
|
|
MaxAge: 30,
|
|
|
|
|
|
},
|
|
|
|
|
|
Security: SecurityConfig{
|
|
|
|
|
|
JWTSecret: "",
|
|
|
|
|
|
EncryptionKey: "",
|
|
|
|
|
|
JWTRefresh: 6,
|
|
|
|
|
|
Cookie: CookieConfig{
|
|
|
|
|
|
Secure: true,
|
|
|
|
|
|
SameSite: "Lax",
|
|
|
|
|
|
Domain: "",
|
|
|
|
|
|
MaxAge: 86400,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetSecureDefaultAppConfig 获取带有安全密钥的默认应用配置
|
|
|
|
|
|
func GetSecureDefaultAppConfig() (*AppConfig, error) {
|
|
|
|
|
|
config := GetDefaultAppConfig()
|
|
|
|
|
|
|
|
|
|
|
|
// 生成安全密钥
|
|
|
|
|
|
jwtSecret, encryptionKey, err := GenerateSecureKeys()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置安全密钥
|
|
|
|
|
|
config.Security.JWTSecret = jwtSecret
|
|
|
|
|
|
config.Security.EncryptionKey = encryptionKey
|
|
|
|
|
|
|
|
|
|
|
|
return config, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Init 初始化配置文件
|
|
|
|
|
|
func Init(cfgFilePath string) {
|
2025-10-31 10:28:25 +08:00
|
|
|
|
viper.SetConfigFile(cfgFilePath)
|
2025-10-27 23:12:15 +08:00
|
|
|
|
viper.SetConfigType("json")
|
|
|
|
|
|
viper.AddConfigPath(".")
|
|
|
|
|
|
|
|
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
|
|
|
|
var pathError *fs.PathError
|
|
|
|
|
|
if errors.As(err, &pathError) {
|
|
|
|
|
|
log.Warn("未找到配置文件,使用默认配置")
|
|
|
|
|
|
|
|
|
|
|
|
// 生成带有安全密钥的默认配置
|
|
|
|
|
|
defaultConfig, configErr := GetSecureDefaultAppConfig()
|
|
|
|
|
|
if configErr != nil {
|
|
|
|
|
|
log.WithFields(
|
|
|
|
|
|
log.Fields{
|
|
|
|
|
|
"err": configErr,
|
|
|
|
|
|
},
|
|
|
|
|
|
).Error("生成安全配置失败,使用基础默认配置")
|
|
|
|
|
|
defaultConfig = GetDefaultAppConfig()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 将配置结构体转换为JSON
|
|
|
|
|
|
configBytes, marshalErr := json.MarshalIndent(defaultConfig, "", " ")
|
|
|
|
|
|
if marshalErr != nil {
|
|
|
|
|
|
log.WithFields(
|
|
|
|
|
|
log.Fields{
|
|
|
|
|
|
"err": marshalErr,
|
|
|
|
|
|
},
|
|
|
|
|
|
).Fatal("序列化默认配置失败")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 10:28:25 +08:00
|
|
|
|
// 写入配置文件
|
|
|
|
|
|
err = os.WriteFile(cfgFilePath, configBytes, 0o644)
|
2025-10-27 23:12:15 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.WithFields(
|
|
|
|
|
|
log.Fields{
|
|
|
|
|
|
"err": err,
|
|
|
|
|
|
},
|
|
|
|
|
|
).Error("写入默认配置文件失败")
|
|
|
|
|
|
} else {
|
2025-10-31 09:38:36 +08:00
|
|
|
|
// 只显示配置文件名,不显示完整路径
|
|
|
|
|
|
fileName := filepath.Base(cfgFilePath)
|
2025-10-27 23:12:15 +08:00
|
|
|
|
log.WithFields(
|
|
|
|
|
|
log.Fields{
|
2025-10-31 09:38:36 +08:00
|
|
|
|
"file": fileName,
|
2025-10-27 23:12:15 +08:00
|
|
|
|
},
|
|
|
|
|
|
).Info("写入默认配置文件成功(已生成安全密钥)")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 将配置加载到viper中
|
|
|
|
|
|
err = viper.ReadConfig(bytes.NewBuffer(configBytes))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.WithFields(
|
|
|
|
|
|
log.Fields{
|
|
|
|
|
|
"err": err,
|
|
|
|
|
|
},
|
|
|
|
|
|
).Error("读取默认配置失败")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
log.Info("已成功读取默认配置")
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
log.WithFields(
|
|
|
|
|
|
log.Fields{
|
|
|
|
|
|
"err": err,
|
|
|
|
|
|
},
|
|
|
|
|
|
).Fatal("配置文件解析错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-31 09:38:36 +08:00
|
|
|
|
|
|
|
|
|
|
// 只显示配置文件名,不显示完整路径
|
|
|
|
|
|
configFile := viper.ConfigFileUsed()
|
|
|
|
|
|
if configFile != "" {
|
|
|
|
|
|
// 提取文件名
|
|
|
|
|
|
fileName := filepath.Base(configFile)
|
|
|
|
|
|
log.WithFields(
|
|
|
|
|
|
log.Fields{
|
|
|
|
|
|
"file": fileName,
|
|
|
|
|
|
},
|
|
|
|
|
|
).Info("使用配置文件")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
log.Info("使用默认配置")
|
|
|
|
|
|
}
|
2025-10-27 23:12:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证配置
|
|
|
|
|
|
if _, err := ValidateConfig(); err != nil {
|
|
|
|
|
|
log.WithFields(
|
|
|
|
|
|
log.Fields{
|
|
|
|
|
|
"err": err,
|
|
|
|
|
|
},
|
2025-10-31 09:38:36 +08:00
|
|
|
|
).Fatal("配置内容验证失败")
|
2025-10-27 23:12:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateDefaultConfig 创建默认配置文件
|
|
|
|
|
|
func CreateDefaultConfig(filePath string) error {
|
|
|
|
|
|
// 生成带有安全密钥的默认配置
|
|
|
|
|
|
defaultConfig, err := GetSecureDefaultAppConfig()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
log.WithFields(
|
|
|
|
|
|
log.Fields{
|
|
|
|
|
|
"err": err,
|
|
|
|
|
|
},
|
|
|
|
|
|
).Error("生成安全配置失败,使用基础默认配置")
|
|
|
|
|
|
defaultConfig = GetDefaultAppConfig()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 将配置结构体转换为JSON
|
|
|
|
|
|
configBytes, err := json.MarshalIndent(defaultConfig, "", " ")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 10:28:25 +08:00
|
|
|
|
return os.WriteFile(filePath, configBytes, 0o644)
|
2025-10-27 23:12:15 +08:00
|
|
|
|
}
|