1b8ff310eb
- Introduced a new API endpoint `GET /api/v1/video/proxy` to facilitate media resource downloads, allowing users to bypass domain restrictions imposed by WeChat. - Updated configuration to include proxy settings such as `SHORT_VIDEO_PROXY_ENABLED`, `SHORT_VIDEO_PROXY_ALLOWED_DOMAINS`, `SHORT_VIDEO_PROXY_MAX_SIZE_MB`, and `SHORT_VIDEO_PROXY_TIMEOUT_SECONDS`. - Enhanced the `ShortVideoConfig` struct to accommodate new proxy-related fields. - Improved error handling for proxy requests, including checks for allowed domains and file size limits. - Updated documentation to reflect the new proxy functionality and its configuration options, ensuring clarity for users and developers.
235 lines
6.6 KiB
Go
Executable File
235 lines
6.6 KiB
Go
Executable File
package config
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
type Config struct {
|
|
Server ServerConfig
|
|
Database DatabaseConfig
|
|
JWT JWTConfig
|
|
ShortVideo ShortVideoConfig
|
|
AI AIConfig
|
|
Admin AdminConfig
|
|
Qiniu QiniuConfig
|
|
WeChatOA WeChatOfficialConfig
|
|
Redis RedisConfig
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
Port string
|
|
Mode string
|
|
}
|
|
|
|
type DatabaseConfig struct {
|
|
Default DatabaseInstanceConfig
|
|
Additional map[string]DatabaseInstanceConfig
|
|
}
|
|
|
|
type DatabaseInstanceConfig struct {
|
|
Host string
|
|
Port string
|
|
User string
|
|
Password string
|
|
DBName string
|
|
}
|
|
|
|
type JWTConfig struct {
|
|
Secret string
|
|
Expire int
|
|
}
|
|
|
|
type ShortVideoConfig struct {
|
|
APIKey string
|
|
FreeDailyQuota int
|
|
RequestTimeout time.Duration
|
|
// 代理相关配置
|
|
ProxyEnabled bool // 是否启用媒体代理
|
|
ProxyAllowedDomains []string // 允许代理的域名白名单(为空表示允许所有)
|
|
ProxyMaxSize int64 // 代理文件最大大小(字节),0 表示不限制
|
|
ProxyTimeout time.Duration // 代理请求超时时间
|
|
}
|
|
|
|
// AIConfig 用于“AI 建议/问答”等能力的通用配置(OpenAI-compatible)。
|
|
type AIConfig struct {
|
|
BaseURL string
|
|
APIKey string
|
|
Model string
|
|
RequestTimeout time.Duration
|
|
}
|
|
|
|
// AdminConfig 用于简单的后台/运维接口鉴权(如生成兑换码)。
|
|
type AdminConfig struct {
|
|
Token string
|
|
}
|
|
|
|
// QiniuConfig 用于七牛云(Kodo)直传相关配置。
|
|
// 前端通常会向后端请求 upload token,然后直传文件到七牛。
|
|
type QiniuConfig struct {
|
|
AccessKey string
|
|
SecretKey string
|
|
Bucket string
|
|
UploadURL string
|
|
CDNDomain string
|
|
KeyPrefix string
|
|
TokenExpireSeconds int
|
|
}
|
|
|
|
// WeChatOfficialConfig 用于微信公众号网页授权(OAuth2)相关接口。
|
|
type WeChatOfficialConfig struct {
|
|
AppID string
|
|
AppSecret string
|
|
RequestTimeout time.Duration
|
|
}
|
|
|
|
// RedisConfig 用于可选的 Redis 缓存(例如缓存 session_key -> user,减少 DB 查询)。
|
|
type RedisConfig struct {
|
|
Addr string
|
|
Password string
|
|
DB int
|
|
KeyPrefix string
|
|
SessionTTLSeconds int
|
|
}
|
|
|
|
var AppConfig *Config
|
|
|
|
func LoadConfig() {
|
|
// 加载 .env 文件
|
|
if err := godotenv.Load(); err != nil {
|
|
log.Println("未找到 .env 文件,使用环境变量")
|
|
}
|
|
|
|
defaultDB := DatabaseInstanceConfig{
|
|
Host: getEnv("DB_HOST", "localhost"),
|
|
Port: getEnv("DB_PORT", "3306"),
|
|
User: getEnv("DB_USER", "root"),
|
|
Password: getEnv("DB_PASSWORD", ""),
|
|
DBName: getEnv("DB_NAME", "wx_service"),
|
|
}
|
|
|
|
AppConfig = &Config{
|
|
Server: ServerConfig{
|
|
Port: getEnv("SERVER_PORT", "8080"),
|
|
Mode: getEnv("GIN_MODE", "debug"),
|
|
},
|
|
Database: DatabaseConfig{
|
|
Default: defaultDB,
|
|
Additional: loadAdditionalDBConfigs(defaultDB),
|
|
},
|
|
JWT: JWTConfig{
|
|
Secret: getEnv("JWT_SECRET", "your-secret-key"),
|
|
Expire: 86400, // 24小时
|
|
},
|
|
ShortVideo: ShortVideoConfig{
|
|
APIKey: getEnv("SHORT_VIDEO_API_KEY", ""),
|
|
FreeDailyQuota: getEnvAsInt("SHORT_VIDEO_FREE_QUOTA", 20),
|
|
RequestTimeout: time.Duration(getEnvAsInt("SHORT_VIDEO_TIMEOUT_SECONDS", 5)) * time.Second,
|
|
ProxyEnabled: getEnvAsBool("SHORT_VIDEO_PROXY_ENABLED", true),
|
|
ProxyAllowedDomains: getEnvAsStringSlice("SHORT_VIDEO_PROXY_ALLOWED_DOMAINS", nil),
|
|
ProxyMaxSize: int64(getEnvAsInt("SHORT_VIDEO_PROXY_MAX_SIZE_MB", 100)) * 1024 * 1024,
|
|
ProxyTimeout: time.Duration(getEnvAsInt("SHORT_VIDEO_PROXY_TIMEOUT_SECONDS", 60)) * time.Second,
|
|
},
|
|
AI: AIConfig{
|
|
BaseURL: getEnv("AI_BASE_URL", "https://api.openai.com/v1"),
|
|
APIKey: getEnv("AI_API_KEY", ""),
|
|
Model: getEnv("AI_MODEL", "gpt-4o-mini"),
|
|
RequestTimeout: time.Duration(getEnvAsInt("AI_TIMEOUT_SECONDS", 15)) * time.Second,
|
|
},
|
|
Admin: AdminConfig{
|
|
Token: getEnv("ADMIN_API_TOKEN", ""),
|
|
},
|
|
Qiniu: QiniuConfig{
|
|
AccessKey: getEnv("QINIU_ACCESS_KEY", ""),
|
|
SecretKey: getEnv("QINIU_SECRET_KEY", ""),
|
|
Bucket: getEnv("QINIU_BUCKET", ""),
|
|
UploadURL: getEnv("QINIU_UPLOAD_URL", "https://upload.qiniup.com"),
|
|
CDNDomain: getEnv("QINIU_CDN_DOMAIN", ""),
|
|
KeyPrefix: getEnv("QINIU_KEY_PREFIX", "uploads/"),
|
|
TokenExpireSeconds: getEnvAsInt("QINIU_TOKEN_EXPIRE_SECONDS", 300),
|
|
},
|
|
WeChatOA: WeChatOfficialConfig{
|
|
AppID: getEnv("WECHAT_OA_APP_ID", ""),
|
|
AppSecret: getEnv("WECHAT_OA_APP_SECRET", ""),
|
|
RequestTimeout: time.Duration(getEnvAsInt("WECHAT_OA_TIMEOUT_SECONDS", 5)) * time.Second,
|
|
},
|
|
Redis: RedisConfig{
|
|
Addr: getEnv("REDIS_ADDR", ""),
|
|
Password: getEnv("REDIS_PASSWORD", ""),
|
|
DB: getEnvAsInt("REDIS_DB", 0),
|
|
KeyPrefix: getEnv("REDIS_KEY_PREFIX", "wx_service:"),
|
|
SessionTTLSeconds: getEnvAsInt("REDIS_SESSION_TTL_SECONDS", 86400),
|
|
},
|
|
}
|
|
}
|
|
|
|
func loadAdditionalDBConfigs(defaultCfg DatabaseInstanceConfig) map[string]DatabaseInstanceConfig {
|
|
instances := strings.Split(getEnv("DB_INSTANCES", ""), ",")
|
|
result := make(map[string]DatabaseInstanceConfig)
|
|
for _, rawName := range instances {
|
|
name := strings.TrimSpace(rawName)
|
|
if name == "" {
|
|
continue
|
|
}
|
|
upperName := strings.ToUpper(name)
|
|
prefix := "DB_" + upperName + "_"
|
|
result[strings.ToLower(name)] = DatabaseInstanceConfig{
|
|
Host: getEnv(prefix+"HOST", defaultCfg.Host),
|
|
Port: getEnv(prefix+"PORT", defaultCfg.Port),
|
|
User: getEnv(prefix+"USER", defaultCfg.User),
|
|
Password: getEnv(prefix+"PASSWORD", defaultCfg.Password),
|
|
DBName: getEnv(prefix+"NAME", defaultCfg.DBName),
|
|
}
|
|
}
|
|
if len(result) == 0 {
|
|
return nil
|
|
}
|
|
return result
|
|
}
|
|
|
|
func getEnv(key, defaultValue string) string {
|
|
if value := os.Getenv(key); value != "" {
|
|
return value
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
func getEnvAsInt(key string, defaultValue int) int {
|
|
if value := os.Getenv(key); value != "" {
|
|
if v, err := strconv.Atoi(value); err == nil {
|
|
return v
|
|
}
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
func getEnvAsBool(key string, defaultValue bool) bool {
|
|
if value := os.Getenv(key); value != "" {
|
|
v := strings.ToLower(value)
|
|
return v == "true" || v == "1" || v == "yes"
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
func getEnvAsStringSlice(key string, defaultValue []string) []string {
|
|
if value := os.Getenv(key); value != "" {
|
|
parts := strings.Split(value, ",")
|
|
result := make([]string, 0, len(parts))
|
|
for _, part := range parts {
|
|
trimmed := strings.TrimSpace(part)
|
|
if trimmed != "" {
|
|
result = append(result, trimmed)
|
|
}
|
|
}
|
|
if len(result) > 0 {
|
|
return result
|
|
}
|
|
}
|
|
return defaultValue
|
|
}
|