package config import ( "fmt" "strings" "time" "github.com/spf13/viper" ) // Config holds all configuration for the MPC system type Config struct { Server ServerConfig `mapstructure:"server"` Database DatabaseConfig `mapstructure:"database"` Redis RedisConfig `mapstructure:"redis"` RabbitMQ RabbitMQConfig `mapstructure:"rabbitmq"` Consul ConsulConfig `mapstructure:"consul"` JWT JWTConfig `mapstructure:"jwt"` MPC MPCConfig `mapstructure:"mpc"` Logger LoggerConfig `mapstructure:"logger"` } // ServerConfig holds server-related configuration type ServerConfig struct { GRPCPort int `mapstructure:"grpc_port"` HTTPPort int `mapstructure:"http_port"` Environment string `mapstructure:"environment"` Timeout time.Duration `mapstructure:"timeout"` TLSEnabled bool `mapstructure:"tls_enabled"` TLSCertFile string `mapstructure:"tls_cert_file"` TLSKeyFile string `mapstructure:"tls_key_file"` } // DatabaseConfig holds database configuration type DatabaseConfig struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` User string `mapstructure:"user"` Password string `mapstructure:"password"` DBName string `mapstructure:"dbname"` SSLMode string `mapstructure:"sslmode"` MaxOpenConns int `mapstructure:"max_open_conns"` MaxIdleConns int `mapstructure:"max_idle_conns"` ConnMaxLife time.Duration `mapstructure:"conn_max_life"` } // DSN returns the database connection string func (c *DatabaseConfig) DSN() string { return fmt.Sprintf( "host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", c.Host, c.Port, c.User, c.Password, c.DBName, c.SSLMode, ) } // RedisConfig holds Redis configuration type RedisConfig struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` Password string `mapstructure:"password"` DB int `mapstructure:"db"` } // Addr returns the Redis address func (c *RedisConfig) Addr() string { return fmt.Sprintf("%s:%d", c.Host, c.Port) } // RabbitMQConfig holds RabbitMQ configuration type RabbitMQConfig struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` User string `mapstructure:"user"` Password string `mapstructure:"password"` VHost string `mapstructure:"vhost"` } // URL returns the RabbitMQ connection URL func (c *RabbitMQConfig) URL() string { return fmt.Sprintf( "amqp://%s:%s@%s:%d/%s", c.User, c.Password, c.Host, c.Port, c.VHost, ) } // ConsulConfig holds Consul configuration type ConsulConfig struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` ServiceID string `mapstructure:"service_id"` Tags []string `mapstructure:"tags"` } // Addr returns the Consul address func (c *ConsulConfig) Addr() string { return fmt.Sprintf("%s:%d", c.Host, c.Port) } // JWTConfig holds JWT configuration type JWTConfig struct { SecretKey string `mapstructure:"secret_key"` Issuer string `mapstructure:"issuer"` TokenExpiry time.Duration `mapstructure:"token_expiry"` RefreshExpiry time.Duration `mapstructure:"refresh_expiry"` } // MPCConfig holds MPC-specific configuration type MPCConfig struct { DefaultThresholdN int `mapstructure:"default_threshold_n"` DefaultThresholdT int `mapstructure:"default_threshold_t"` SessionTimeout time.Duration `mapstructure:"session_timeout"` MessageTimeout time.Duration `mapstructure:"message_timeout"` KeygenTimeout time.Duration `mapstructure:"keygen_timeout"` SigningTimeout time.Duration `mapstructure:"signing_timeout"` MaxParties int `mapstructure:"max_parties"` } // LoggerConfig holds logger configuration type LoggerConfig struct { Level string `mapstructure:"level"` Encoding string `mapstructure:"encoding"` OutputPath string `mapstructure:"output_path"` } // Load loads configuration from file and environment variables func Load(configPath string) (*Config, error) { v := viper.New() // Set default values setDefaults(v) // Read config file if configPath != "" { v.SetConfigFile(configPath) } else { v.SetConfigName("config") v.SetConfigType("yaml") v.AddConfigPath(".") v.AddConfigPath("./config") v.AddConfigPath("/etc/mpc-system/") } // Read environment variables v.SetEnvPrefix("MPC") v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) v.AutomaticEnv() // Read config file (if exists) if err := v.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); !ok { return nil, fmt.Errorf("failed to read config file: %w", err) } // Config file not found is not an error, we'll use defaults + env vars } var config Config if err := v.Unmarshal(&config); err != nil { return nil, fmt.Errorf("failed to unmarshal config: %w", err) } return &config, nil } // setDefaults sets default configuration values func setDefaults(v *viper.Viper) { // Server defaults v.SetDefault("server.grpc_port", 50051) v.SetDefault("server.http_port", 8080) v.SetDefault("server.environment", "development") v.SetDefault("server.timeout", "30s") v.SetDefault("server.tls_enabled", false) // Database defaults v.SetDefault("database.host", "localhost") v.SetDefault("database.port", 5432) v.SetDefault("database.user", "mpc_user") v.SetDefault("database.password", "") v.SetDefault("database.dbname", "mpc_system") v.SetDefault("database.sslmode", "disable") v.SetDefault("database.max_open_conns", 25) v.SetDefault("database.max_idle_conns", 5) v.SetDefault("database.conn_max_life", "5m") // Redis defaults v.SetDefault("redis.host", "localhost") v.SetDefault("redis.port", 6379) v.SetDefault("redis.password", "") v.SetDefault("redis.db", 0) // RabbitMQ defaults v.SetDefault("rabbitmq.host", "localhost") v.SetDefault("rabbitmq.port", 5672) v.SetDefault("rabbitmq.user", "guest") v.SetDefault("rabbitmq.password", "guest") v.SetDefault("rabbitmq.vhost", "/") // Consul defaults v.SetDefault("consul.host", "localhost") v.SetDefault("consul.port", 8500) // JWT defaults v.SetDefault("jwt.issuer", "mpc-system") v.SetDefault("jwt.token_expiry", "15m") v.SetDefault("jwt.refresh_expiry", "24h") // MPC defaults v.SetDefault("mpc.default_threshold_n", 3) v.SetDefault("mpc.default_threshold_t", 2) v.SetDefault("mpc.session_timeout", "10m") v.SetDefault("mpc.message_timeout", "30s") v.SetDefault("mpc.keygen_timeout", "10m") v.SetDefault("mpc.signing_timeout", "5m") v.SetDefault("mpc.max_parties", 10) // Logger defaults v.SetDefault("logger.level", "info") v.SetDefault("logger.encoding", "json") v.SetDefault("logger.output_path", "stdout") } // MustLoad loads configuration and panics on error func MustLoad(configPath string) *Config { cfg, err := Load(configPath) if err != nil { panic(fmt.Sprintf("failed to load config: %v", err)) } return cfg }