Files
subconverter-go/internal/config/model.go
Rogee 7fcabe0225
Some checks failed
CI/CD Pipeline / Test (push) Failing after 22m19s
CI/CD Pipeline / Security Scan (push) Failing after 5m57s
CI/CD Pipeline / Build (amd64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (amd64, linux) (push) Has been skipped
CI/CD Pipeline / Build (amd64, windows) (push) Has been skipped
CI/CD Pipeline / Build (arm64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (arm64, linux) (push) Has been skipped
CI/CD Pipeline / Build Docker Image (push) Has been skipped
CI/CD Pipeline / Create Release (push) Has been skipped
first commit
2025-09-28 10:05:07 +08:00

450 lines
13 KiB
Go
Raw 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 (
"fmt"
"os"
"strconv"
"strings"
"time"
)
// Configuration 表示应用程序的完整配置
// 该结构体包含服务器、日志、安全和转换相关的所有配置项
type Configuration struct {
// ServerConfig 服务器配置
Server ServerConfig `yaml:"server" json:"server"`
// LoggingConfig 日志配置
Logging LoggingConfig `yaml:"logging" json:"logging"`
// SecurityConfig 安全配置
Security SecurityConfig `yaml:"security" json:"security"`
// ConversionConfig 转换配置
Conversion ConversionConfig `yaml:"conversion" json:"conversion"`
}
// ServerConfig 服务器相关配置
type ServerConfig struct {
// Port 服务器监听端口默认25500
Port int `yaml:"port" json:"port" env:"SUBCONVERTER_PORT" mapstructure:"port"`
// Host 服务器监听地址,默认"0.0.0.0"
Host string `yaml:"host" json:"host" env:"SUBCONVERTER_HOST" mapstructure:"host"`
// ReadTimeout 读取超时时间默认30
ReadTimeout int `yaml:"read_timeout" json:"read_timeout" env:"SUBCONVERTER_READ_TIMEOUT" mapstructure:"read_timeout"`
// WriteTimeout 写入超时时间默认30
WriteTimeout int `yaml:"write_timeout" json:"write_timeout" env:"SUBCONVERTER_WRITE_TIMEOUT" mapstructure:"write_timeout"`
// MaxRequestSize 最大请求大小字节默认10MB
MaxRequestSize int64 `yaml:"max_request_size" json:"max_request_size" env:"SUBCONVERTER_MAX_REQUEST_SIZE" mapstructure:"max_request_size"`
}
// Clone 创建服务器配置的深拷贝
func (s *ServerConfig) Clone() ServerConfig {
if s == nil {
return ServerConfig{}
}
return ServerConfig{
Port: s.Port,
Host: s.Host,
ReadTimeout: s.ReadTimeout,
WriteTimeout: s.WriteTimeout,
MaxRequestSize: s.MaxRequestSize,
}
}
// LoggingConfig 日志相关配置
type LoggingConfig struct {
// Level 日志级别可选debug, info, warn, error默认info
Level string `yaml:"level" json:"level" env:"SUBCONVERTER_LOG_LEVEL" mapstructure:"level"`
// Format 日志格式可选json, text默认json
Format string `yaml:"format" json:"format" env:"SUBCONVERTER_LOG_FORMAT" mapstructure:"format"`
// Output 日志输出可选stdout, stderr, file默认stdout
Output string `yaml:"output" json:"output" env:"SUBCONVERTER_LOG_OUTPUT" mapstructure:"output"`
// File 如果Output为file指定日志文件路径
File string `yaml:"file" json:"file" env:"SUBCONVERTER_LOG_FILE" mapstructure:"file"`
}
// Clone 创建日志配置的深拷贝
func (l *LoggingConfig) Clone() LoggingConfig {
if l == nil {
return LoggingConfig{}
}
return LoggingConfig{
Level: l.Level,
Format: l.Format,
Output: l.Output,
File: l.File,
}
}
// SecurityConfig 安全相关配置
type SecurityConfig struct {
// AccessTokens 访问令牌列表,空列表表示无需认证
AccessTokens []string `yaml:"access_tokens" json:"access_tokens" env:"SUBCONVERTER_ACCESS_TOKENS" mapstructure:"access_tokens"`
// CorsOrigins CORS允许的源列表支持通配符*
CorsOrigins []string `yaml:"cors_origins" json:"cors_origins" env:"SUBCONVERTER_CORS_ORIGINS" mapstructure:"cors_origins"`
// RateLimit 速率限制每分钟请求数0表示不限制
RateLimit int `yaml:"rate_limit" json:"rate_limit" env:"SUBCONVERTER_RATE_LIMIT" mapstructure:"rate_limit"`
// Timeout 请求超时时间默认60
Timeout int `yaml:"timeout" json:"timeout" env:"SUBCONVERTER_TIMEOUT" mapstructure:"timeout"`
}
// Clone 创建安全配置的深拷贝
func (s *SecurityConfig) Clone() SecurityConfig {
if s == nil {
return SecurityConfig{}
}
// 深拷贝切片
accessTokens := make([]string, len(s.AccessTokens))
copy(accessTokens, s.AccessTokens)
corsOrigins := make([]string, len(s.CorsOrigins))
copy(corsOrigins, s.CorsOrigins)
return SecurityConfig{
AccessTokens: accessTokens,
CorsOrigins: corsOrigins,
RateLimit: s.RateLimit,
Timeout: s.Timeout,
}
}
// ConversionConfig 转换相关配置
type ConversionConfig struct {
// DefaultTarget 默认目标格式默认clash
DefaultTarget string `yaml:"default_target" json:"default_target" env:"SUBCONVERTER_DEFAULT_TARGET" mapstructure:"default_target"`
// SupportedTargets 支持的目标格式列表
SupportedTargets []string `yaml:"supported_targets" json:"supported_targets" mapstructure:"supported_targets"`
// DefaultEmoji 默认是否启用emoji默认false
DefaultEmoji bool `yaml:"default_emoji" json:"default_emoji" env:"SUBCONVERTER_DEFAULT_EMOJI" mapstructure:"default_emoji"`
// DefaultUDP 默认是否启用UDP默认false
DefaultUDP bool `yaml:"default_udp" json:"default_udp" env:"SUBCONVERTER_DEFAULT_UDP" mapstructure:"default_udp"`
// MaxNodes 最大节点数量默认0无限制
MaxNodes int `yaml:"max_nodes" json:"max_nodes" env:"SUBCONVERTER_MAX_NODES" mapstructure:"max_nodes"`
// CacheTimeout 缓存超时时间分钟默认60
CacheTimeout int `yaml:"cache_timeout" json:"cache_timeout" env:"SUBCONVERTER_CACHE_TIMEOUT" mapstructure:"cache_timeout"`
}
// Clone 创建转换配置的深拷贝
func (c *ConversionConfig) Clone() ConversionConfig {
if c == nil {
return ConversionConfig{}
}
// 深拷贝切片
supportedTargets := make([]string, len(c.SupportedTargets))
copy(supportedTargets, c.SupportedTargets)
return ConversionConfig{
DefaultTarget: c.DefaultTarget,
SupportedTargets: supportedTargets,
DefaultEmoji: c.DefaultEmoji,
DefaultUDP: c.DefaultUDP,
MaxNodes: c.MaxNodes,
CacheTimeout: c.CacheTimeout,
}
}
// NewConfiguration 创建新的默认配置
// 返回包含所有默认值的Configuration结构体
func NewConfiguration() *Configuration {
return &Configuration{
Server: ServerConfig{
Port: 25500,
Host: "0.0.0.0",
ReadTimeout: 30,
WriteTimeout: 30,
MaxRequestSize: 10 * 1024 * 1024, // 10MB
},
Logging: LoggingConfig{
Level: "info",
Format: "json",
Output: "stdout",
File: "",
},
Security: SecurityConfig{
AccessTokens: []string{},
CorsOrigins: []string{"*"},
RateLimit: 0,
Timeout: 60,
},
Conversion: ConversionConfig{
DefaultTarget: "clash",
SupportedTargets: []string{"clash", "clashr", "surge", "quanx", "loon", "surfboard", "v2ray"},
DefaultEmoji: false,
DefaultUDP: false,
MaxNodes: 0,
CacheTimeout: 60,
},
}
}
// Validate 验证配置的有效性
// 返回error如果配置无效nil表示配置有效
func (c *Configuration) Validate() error {
// 验证服务器配置
if c.Server.Port < 1 || c.Server.Port > 65535 {
return fmt.Errorf("server port must be between 1 and 65535, got: %d", c.Server.Port)
}
if c.Server.Host == "" {
return fmt.Errorf("server host cannot be empty")
}
if c.Server.ReadTimeout < 1 {
return fmt.Errorf("read timeout must be positive, got: %d", c.Server.ReadTimeout)
}
if c.Server.WriteTimeout < 1 {
return fmt.Errorf("write timeout must be positive, got: %d", c.Server.WriteTimeout)
}
if c.Server.MaxRequestSize < 1 {
return fmt.Errorf("max request size must be positive, got: %d", c.Server.MaxRequestSize)
}
// 验证日志配置
validLogLevels := map[string]bool{
"debug": true,
"info": true,
"warn": true,
"error": true,
}
if !validLogLevels[c.Logging.Level] {
return fmt.Errorf("log level must be one of: debug, info, warn, error, got: %s", c.Logging.Level)
}
validLogFormats := map[string]bool{
"json": true,
"text": true,
}
if !validLogFormats[c.Logging.Format] {
return fmt.Errorf("log format must be one of: json, text, got: %s", c.Logging.Format)
}
validOutputs := map[string]bool{
"stdout": true,
"stderr": true,
"file": true,
}
if !validOutputs[c.Logging.Output] {
return fmt.Errorf("log output must be one of: stdout, stderr, file, got: %s", c.Logging.Output)
}
if c.Logging.Output == "file" && c.Logging.File == "" {
return fmt.Errorf("log file path must be specified when output is file")
}
// 验证安全配置
if c.Security.Timeout < 1 {
return fmt.Errorf("timeout must be positive, got: %d", c.Security.Timeout)
}
if c.Security.RateLimit < 0 {
return fmt.Errorf("rate limit must be non-negative, got: %d", c.Security.RateLimit)
}
// 验证转换配置
if !contains(c.Conversion.SupportedTargets, c.Conversion.DefaultTarget) {
return fmt.Errorf("default target must be in supported targets, got: %s", c.Conversion.DefaultTarget)
}
if c.Conversion.MaxNodes < 0 {
return fmt.Errorf("max nodes must be non-negative, got: %d", c.Conversion.MaxNodes)
}
if c.Conversion.CacheTimeout < 0 {
return fmt.Errorf("cache timeout must be non-negative, got: %d", c.Conversion.CacheTimeout)
}
return nil
}
// Clone 创建配置的深拷贝
// 返回配置的独立副本
func (c *Configuration) Clone() *Configuration {
if c == nil {
return nil
}
clone := &Configuration{
Server: c.Server.Clone(),
Logging: c.Logging.Clone(),
Security: c.Security.Clone(),
Conversion: c.Conversion.Clone(),
}
return clone
}
// LoadFromEnvironment 从环境变量加载配置
// 根据环境变量覆盖默认配置值
func (c *Configuration) LoadFromEnvironment() {
// 服务器配置
if port := os.Getenv("SUBCONVERTER_PORT"); port != "" {
if p, err := strconv.Atoi(port); err == nil {
c.Server.Port = p
}
}
if host := os.Getenv("SUBCONVERTER_HOST"); host != "" {
c.Server.Host = host
}
if readTimeout := os.Getenv("SUBCONVERTER_READ_TIMEOUT"); readTimeout != "" {
if rt, err := strconv.Atoi(readTimeout); err == nil {
c.Server.ReadTimeout = rt
}
}
if writeTimeout := os.Getenv("SUBCONVERTER_WRITE_TIMEOUT"); writeTimeout != "" {
if wt, err := strconv.Atoi(writeTimeout); err == nil {
c.Server.WriteTimeout = wt
}
}
// 日志配置
if logLevel := os.Getenv("SUBCONVERTER_LOG_LEVEL"); logLevel != "" {
c.Logging.Level = logLevel
}
if logFormat := os.Getenv("SUBCONVERTER_LOG_FORMAT"); logFormat != "" {
c.Logging.Format = logFormat
}
if logOutput := os.Getenv("SUBCONVERTER_LOG_OUTPUT"); logOutput != "" {
c.Logging.Output = logOutput
}
if logFile := os.Getenv("SUBCONVERTER_LOG_FILE"); logFile != "" {
c.Logging.File = logFile
}
// 安全配置
if accessTokens := os.Getenv("SUBCONVERTER_ACCESS_TOKENS"); accessTokens != "" {
c.Security.AccessTokens = strings.Split(accessTokens, ",")
}
if corsOrigins := os.Getenv("SUBCONVERTER_CORS_ORIGINS"); corsOrigins != "" {
c.Security.CorsOrigins = strings.Split(corsOrigins, ",")
}
if rateLimit := os.Getenv("SUBCONVERTER_RATE_LIMIT"); rateLimit != "" {
if rl, err := strconv.Atoi(rateLimit); err == nil {
c.Security.RateLimit = rl
}
}
if timeout := os.Getenv("SUBCONVERTER_TIMEOUT"); timeout != "" {
if t, err := strconv.Atoi(timeout); err == nil {
c.Security.Timeout = t
}
}
// 转换配置
if defaultTarget := os.Getenv("SUBCONVERTER_DEFAULT_TARGET"); defaultTarget != "" {
c.Conversion.DefaultTarget = defaultTarget
}
if defaultEmoji := os.Getenv("SUBCONVERTER_DEFAULT_EMOJI"); defaultEmoji != "" {
if de, err := strconv.ParseBool(defaultEmoji); err == nil {
c.Conversion.DefaultEmoji = de
}
}
if defaultUDP := os.Getenv("SUBCONVERTER_DEFAULT_UDP"); defaultUDP != "" {
if du, err := strconv.ParseBool(defaultUDP); err == nil {
c.Conversion.DefaultUDP = du
}
}
if maxNodes := os.Getenv("SUBCONVERTER_MAX_NODES"); maxNodes != "" {
if mn, err := strconv.Atoi(maxNodes); err == nil {
c.Conversion.MaxNodes = mn
}
}
if cacheTimeout := os.Getenv("SUBCONVERTER_CACHE_TIMEOUT"); cacheTimeout != "" {
if ct, err := strconv.Atoi(cacheTimeout); err == nil {
c.Conversion.CacheTimeout = ct
}
}
}
// GetServerAddress 返回服务器地址字符串
// 格式为 "host:port"
func (c *Configuration) GetServerAddress() string {
return fmt.Sprintf("%s:%d", c.Server.Host, c.Server.Port)
}
// GetReadTimeoutDuration 返回读取超时时间duration
func (c *Configuration) GetReadTimeoutDuration() time.Duration {
return time.Duration(c.Server.ReadTimeout) * time.Second
}
// GetWriteTimeoutDuration 返回写入超时时间duration
func (c *Configuration) GetWriteTimeoutDuration() time.Duration {
return time.Duration(c.Server.WriteTimeout) * time.Second
}
// GetTimeoutDuration 返回请求超时时间duration
func (c *Configuration) GetTimeoutDuration() time.Duration {
return time.Duration(c.Security.Timeout) * time.Second
}
// GetCacheTimeoutDuration 返回缓存超时时间duration
func (c *Configuration) GetCacheTimeoutDuration() time.Duration {
return time.Duration(c.Conversion.CacheTimeout) * time.Minute
}
// IsAccessTokenValid 检查访问令牌是否有效
func (c *Configuration) IsAccessTokenValid(token string) bool {
if len(c.Security.AccessTokens) == 0 {
return true // 空列表表示无需认证
}
return contains(c.Security.AccessTokens, token)
}
// IsCorsOriginAllowed 检查CORS源是否允许
func (c *Configuration) IsCorsOriginAllowed(origin string) bool {
for _, allowed := range c.Security.CorsOrigins {
if allowed == "*" || allowed == origin {
return true
}
}
return false
}
// IsTargetSupported 检查目标格式是否支持
func (c *Configuration) IsTargetSupported(target string) bool {
return contains(c.Conversion.SupportedTargets, target)
}
// contains 检查字符串切片是否包含特定字符串
func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}