655c1d366e
- 公开商品页(/product/:uuid)全面重设计:全宽正方形图片轮播、 左右滑动切图、点击放大全屏查看、商品参数含描述、页脚贴底 - 修复 Flutter web 文件上传无反应(path→bytes) - 修复 web 路由空白页(usePathUrlStrategy + 单层 MaterialApp.router) - 二维码 URL 改为从 STORAGE_PUBLIC_URL 环境变量读取 - 新增 PUBLIC_URL dart-define → AppConfig.publicBaseUrl - 新增 CI/CD workflows + NAS runner compose 配置 - seed S001 补充商品描述字段 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
82 lines
2.4 KiB
Go
82 lines
2.4 KiB
Go
package config
|
|
|
|
import (
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
type Config struct {
|
|
Server ServerConfig
|
|
Database DatabaseConfig
|
|
JWT JWTConfig
|
|
License LicenseConfig
|
|
Storage StorageConfig
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
Port string `mapstructure:"port"`
|
|
Mode string `mapstructure:"mode"` // debug | release
|
|
CORSOrigin string `mapstructure:"cors_origin"` // 允许的 CORS 来源,生产设为具体域名
|
|
}
|
|
|
|
type DatabaseConfig struct {
|
|
DSN string `mapstructure:"dsn"`
|
|
}
|
|
|
|
type JWTConfig struct {
|
|
Secret string `mapstructure:"secret"`
|
|
AccessExpireMin int `mapstructure:"access_expire_min"` // Access Token 有效分钟数
|
|
RefreshExpireH int `mapstructure:"refresh_expire_h"` // Refresh Token 有效小时数
|
|
}
|
|
|
|
type LicenseConfig struct {
|
|
HMACSecret string `mapstructure:"hmac_secret"` // 许可证签名密钥
|
|
}
|
|
|
|
type StorageConfig struct {
|
|
UploadDir string `mapstructure:"upload_dir"`
|
|
BaseURL string `mapstructure:"base_url"`
|
|
PublicURL string `mapstructure:"public_url"` // 商品公开页基础 URL,用于生成二维码
|
|
}
|
|
|
|
var C Config
|
|
|
|
func Load() {
|
|
viper.SetConfigName("config")
|
|
viper.SetConfigType("yaml")
|
|
viper.AddConfigPath(".")
|
|
viper.AddConfigPath("./config")
|
|
|
|
// 环境变量覆盖(生产部署时使用)
|
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
viper.AutomaticEnv()
|
|
|
|
// 显式绑定没有默认值的 key,确保 AutomaticEnv 能找到对应 env var
|
|
_ = viper.BindEnv("database.dsn", "DATABASE_DSN")
|
|
_ = viper.BindEnv("jwt.secret", "JWT_SECRET")
|
|
_ = viper.BindEnv("license.hmac_secret", "LICENSE_HMAC_SECRET")
|
|
_ = viper.BindEnv("storage.upload_dir", "STORAGE_UPLOAD_DIR")
|
|
_ = viper.BindEnv("storage.base_url", "STORAGE_BASE_URL")
|
|
_ = viper.BindEnv("storage.public_url", "STORAGE_PUBLIC_URL")
|
|
|
|
// 默认值
|
|
viper.SetDefault("server.port", "8080")
|
|
viper.SetDefault("server.mode", "debug")
|
|
viper.SetDefault("server.cors_origin", "*")
|
|
viper.SetDefault("jwt.access_expire_min", 60)
|
|
viper.SetDefault("jwt.refresh_expire_h", 168) // 7天
|
|
viper.SetDefault("storage.upload_dir", "./uploads/images")
|
|
viper.SetDefault("storage.base_url", "http://localhost:8080/images")
|
|
viper.SetDefault("storage.public_url", "http://localhost:8081")
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
log.Println("[config] no config file found, using defaults and env vars")
|
|
}
|
|
|
|
if err := viper.Unmarshal(&C); err != nil {
|
|
log.Fatalf("[config] failed to unmarshal config: %v", err)
|
|
}
|
|
}
|