Files
go-common/config/config.go
2025-11-30 13:43:43 +08:00

501 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"git.toowon.com/jimmy/go-common/middleware"
)
// Config 应用配置
type Config struct {
Database *DatabaseConfig `json:"database"`
OSS *OSSConfig `json:"oss"`
Redis *RedisConfig `json:"redis"`
CORS *CORSConfig `json:"cors"`
MinIO *MinIOConfig `json:"minio"`
Email *EmailConfig `json:"email"`
SMS *SMSConfig `json:"sms"`
Logger *LoggerConfig `json:"logger"`
}
// DatabaseConfig 数据库配置
type DatabaseConfig struct {
// Type 数据库类型: mysql, postgres, sqlite
Type string `json:"type"`
// Host 数据库主机
Host string `json:"host"`
// Port 数据库端口
Port int `json:"port"`
// User 数据库用户名
User string `json:"user"`
// Password 数据库密码
Password string `json:"password"`
// Database 数据库名称
Database string `json:"database"`
// Charset 字符集MySQL使用
Charset string `json:"charset"`
// MaxOpenConns 最大打开连接数
MaxOpenConns int `json:"maxOpenConns"`
// MaxIdleConns 最大空闲连接数
MaxIdleConns int `json:"maxIdleConns"`
// ConnMaxLifetime 连接最大生存时间(秒)
ConnMaxLifetime int `json:"connMaxLifetime"`
// DSN 数据库连接字符串(如果设置了,会优先使用)
DSN string `json:"dsn"`
}
// OSSConfig OSS对象存储配置
type OSSConfig struct {
// Provider 提供商: aliyun, tencent, aws, qiniu
Provider string `json:"provider"`
// Endpoint 端点地址
Endpoint string `json:"endpoint"`
// AccessKeyID 访问密钥ID
AccessKeyID string `json:"accessKeyId"`
// AccessKeySecret 访问密钥
AccessKeySecret string `json:"accessKeySecret"`
// Bucket 存储桶名称
Bucket string `json:"bucket"`
// Region 区域
Region string `json:"region"`
// UseSSL 是否使用SSL
UseSSL bool `json:"useSSL"`
// Domain 自定义域名CDN域名
Domain string `json:"domain"`
}
// RedisConfig Redis配置
type RedisConfig struct {
// Host Redis主机
Host string `json:"host"`
// Port Redis端口
Port int `json:"port"`
// Password Redis密码
Password string `json:"password"`
// Database Redis数据库编号
Database int `json:"database"`
// MaxRetries 最大重试次数
MaxRetries int `json:"maxRetries"`
// PoolSize 连接池大小
PoolSize int `json:"poolSize"`
// MinIdleConns 最小空闲连接数
MinIdleConns int `json:"minIdleConns"`
// DialTimeout 连接超时时间(秒)
DialTimeout int `json:"dialTimeout"`
// ReadTimeout 读取超时时间(秒)
ReadTimeout int `json:"readTimeout"`
// WriteTimeout 写入超时时间(秒)
WriteTimeout int `json:"writeTimeout"`
// Addr Redis地址如果设置了会优先使用格式: host:port
Addr string `json:"addr"`
}
// CORSConfig CORS配置与middleware.CORSConfig兼容
type CORSConfig struct {
// AllowedOrigins 允许的源
AllowedOrigins []string `json:"allowedOrigins"`
// AllowedMethods 允许的HTTP方法
AllowedMethods []string `json:"allowedMethods"`
// AllowedHeaders 允许的请求头
AllowedHeaders []string `json:"allowedHeaders"`
// ExposedHeaders 暴露给客户端的响应头
ExposedHeaders []string `json:"exposedHeaders"`
// AllowCredentials 是否允许发送凭证
AllowCredentials bool `json:"allowCredentials"`
// MaxAge 预检请求的缓存时间(秒)
MaxAge int `json:"maxAge"`
}
// MinIOConfig MinIO配置
type MinIOConfig struct {
// Endpoint MinIO端点地址
Endpoint string `json:"endpoint"`
// AccessKeyID 访问密钥ID
AccessKeyID string `json:"accessKeyId"`
// SecretAccessKey 密钥
SecretAccessKey string `json:"secretAccessKey"`
// UseSSL 是否使用SSL
UseSSL bool `json:"useSSL"`
// Bucket 存储桶名称
Bucket string `json:"bucket"`
// Region 区域
Region string `json:"region"`
// Domain 自定义域名
Domain string `json:"domain"`
}
// EmailConfig 邮件配置
type EmailConfig struct {
// Host SMTP服务器地址
Host string `json:"host"`
// Port SMTP服务器端口
Port int `json:"port"`
// Username 发件人邮箱
Username string `json:"username"`
// Password 邮箱密码或授权码
Password string `json:"password"`
// From 发件人邮箱地址如果为空使用Username
From string `json:"from"`
// FromName 发件人名称
FromName string `json:"fromName"`
// UseTLS 是否使用TLS
UseTLS bool `json:"useTLS"`
// UseSSL 是否使用SSL
UseSSL bool `json:"useSSL"`
// Timeout 连接超时时间(秒)
Timeout int `json:"timeout"`
}
// SMSConfig 短信配置(阿里云短信)
type SMSConfig struct {
// AccessKeyID 阿里云AccessKey ID
AccessKeyID string `json:"accessKeyId"`
// AccessKeySecret 阿里云AccessKey Secret
AccessKeySecret string `json:"accessKeySecret"`
// Region 区域cn-hangzhou
Region string `json:"region"`
// SignName 短信签名
SignName string `json:"signName"`
// TemplateCode 短信模板代码
TemplateCode string `json:"templateCode"`
// Endpoint 服务端点(可选,默认使用区域端点)
Endpoint string `json:"endpoint"`
// Timeout 请求超时时间(秒)
Timeout int `json:"timeout"`
}
// LoggerConfig 日志配置
type LoggerConfig struct {
// Level 日志级别: debug, info, warn, error
Level string `json:"level"`
// Output 输出方式: stdout, stderr, file, both
Output string `json:"output"`
// FilePath 日志文件路径当output为file或both时必需
FilePath string `json:"filePath"`
// Prefix 日志前缀
Prefix string `json:"prefix"`
// DisableTimestamp 禁用时间戳
DisableTimestamp bool `json:"disableTimestamp"`
}
// LoadFromFile 从文件加载配置
// filePath: 配置文件路径(支持绝对路径和相对路径)
func LoadFromFile(filePath string) (*Config, error) {
// 转换为绝对路径
absPath, err := filepath.Abs(filePath)
if err != nil {
return nil, fmt.Errorf("failed to get absolute path: %w", err)
}
// 读取文件
data, err := os.ReadFile(absPath)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}
// 解析JSON
var config Config
if err := json.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}
// 设置默认值
config.setDefaults()
return &config, nil
}
// LoadFromBytes 从字节数组加载配置
func LoadFromBytes(data []byte) (*Config, error) {
var config Config
if err := json.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("failed to parse config: %w", err)
}
// 设置默认值
config.setDefaults()
return &config, nil
}
// setDefaults 设置默认值
func (c *Config) setDefaults() {
// 数据库默认值
if c.Database != nil {
if c.Database.Charset == "" {
c.Database.Charset = "utf8mb4"
}
if c.Database.MaxOpenConns == 0 {
c.Database.MaxOpenConns = 100
}
if c.Database.MaxIdleConns == 0 {
c.Database.MaxIdleConns = 10
}
if c.Database.ConnMaxLifetime == 0 {
c.Database.ConnMaxLifetime = 3600
}
}
// Redis默认值
if c.Redis != nil {
if c.Redis.Port == 0 {
c.Redis.Port = 6379
}
if c.Redis.Database == 0 {
c.Redis.Database = 0
}
if c.Redis.MaxRetries == 0 {
c.Redis.MaxRetries = 3
}
if c.Redis.PoolSize == 0 {
c.Redis.PoolSize = 10
}
if c.Redis.MinIdleConns == 0 {
c.Redis.MinIdleConns = 5
}
if c.Redis.DialTimeout == 0 {
c.Redis.DialTimeout = 5
}
if c.Redis.ReadTimeout == 0 {
c.Redis.ReadTimeout = 3
}
if c.Redis.WriteTimeout == 0 {
c.Redis.WriteTimeout = 3
}
}
// CORS默认值
if c.CORS != nil {
if len(c.CORS.AllowedOrigins) == 0 {
c.CORS.AllowedOrigins = []string{"*"}
}
if len(c.CORS.AllowedMethods) == 0 {
c.CORS.AllowedMethods = []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"}
}
if len(c.CORS.AllowedHeaders) == 0 {
c.CORS.AllowedHeaders = []string{"Content-Type", "Authorization", "X-Requested-With", "X-Timezone"}
}
if c.CORS.MaxAge == 0 {
c.CORS.MaxAge = 86400
}
}
// 邮件默认值
if c.Email != nil {
if c.Email.Port == 0 {
c.Email.Port = 587 // 默认使用587端口TLS
}
if c.Email.From == "" {
c.Email.From = c.Email.Username
}
if c.Email.Timeout == 0 {
c.Email.Timeout = 30
}
}
// 短信默认值
if c.SMS != nil {
if c.SMS.Region == "" {
c.SMS.Region = "cn-hangzhou"
}
if c.SMS.Timeout == 0 {
c.SMS.Timeout = 10
}
}
// 日志默认值
if c.Logger != nil {
if c.Logger.Level == "" {
c.Logger.Level = "info"
}
if c.Logger.Output == "" {
c.Logger.Output = "stdout"
}
}
}
// GetDatabase 获取数据库配置
func (c *Config) GetDatabase() *DatabaseConfig {
return c.Database
}
// GetOSS 获取OSS配置
func (c *Config) GetOSS() *OSSConfig {
return c.OSS
}
// GetRedis 获取Redis配置
func (c *Config) GetRedis() *RedisConfig {
return c.Redis
}
// GetCORS 获取CORS配置并转换为middleware.CORSConfig
func (c *Config) GetCORS() *middleware.CORSConfig {
if c.CORS == nil {
return middleware.DefaultCORSConfig()
}
return &middleware.CORSConfig{
AllowedOrigins: c.CORS.AllowedOrigins,
AllowedMethods: c.CORS.AllowedMethods,
AllowedHeaders: c.CORS.AllowedHeaders,
ExposedHeaders: c.CORS.ExposedHeaders,
AllowCredentials: c.CORS.AllowCredentials,
MaxAge: c.CORS.MaxAge,
}
}
// GetMinIO 获取MinIO配置
func (c *Config) GetMinIO() *MinIOConfig {
return c.MinIO
}
// GetEmail 获取邮件配置
func (c *Config) GetEmail() *EmailConfig {
return c.Email
}
// GetSMS 获取短信配置
func (c *Config) GetSMS() *SMSConfig {
return c.SMS
}
// GetLogger 获取日志配置
func (c *Config) GetLogger() *LoggerConfig {
return c.Logger
}
// GetDatabaseDSN 获取数据库连接字符串
func (c *Config) GetDatabaseDSN() (string, error) {
if c.Database == nil {
return "", fmt.Errorf("database config is nil")
}
// 如果已经设置了DSN直接返回
if c.Database.DSN != "" {
return c.Database.DSN, nil
}
// 根据数据库类型生成DSN
switch c.Database.Type {
case "mysql":
return c.buildMySQLDSN(), nil
case "postgres":
return c.buildPostgresDSN(), nil
case "sqlite":
return c.Database.Database, nil
default:
return "", fmt.Errorf("unsupported database type: %s", c.Database.Type)
}
}
// buildMySQLDSN 构建MySQL连接字符串
// 注意数据库时间统一使用UTC时间不设置时区
func (c *Config) buildMySQLDSN() string {
db := c.Database
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", db.User, db.Password, db.Host, db.Port, db.Database)
params := []string{}
if db.Charset != "" {
params = append(params, "charset="+db.Charset)
}
params = append(params, "parseTime=True")
params = append(params, "loc=UTC") // 统一使用UTC时区
if len(params) > 0 {
dsn += "?" + params[0]
for i := 1; i < len(params); i++ {
dsn += "&" + params[i]
}
}
return dsn
}
// buildPostgresDSN 构建PostgreSQL连接字符串
// 注意数据库时间统一使用UTC时间不设置时区
func (c *Config) buildPostgresDSN() string {
db := c.Database
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s",
db.Host, db.Port, db.User, db.Password, db.Database)
// 统一使用UTC时区
dsn += " timezone=UTC"
dsn += " sslmode=disable"
return dsn
}
// GetRedisAddr 获取Redis地址
func (c *Config) GetRedisAddr() string {
if c.Redis == nil {
return ""
}
// 如果已经设置了Addr直接返回
if c.Redis.Addr != "" {
return c.Redis.Addr
}
// 构建地址
return fmt.Sprintf("%s:%d", c.Redis.Host, c.Redis.Port)
}