UnifiedConfig System Guide¶
Architecture Overview¶
UnifiedConfig принципы:
- ✅ Single Source of Truth - все настройки в одном месте
- ✅ Modular Structure - 7 специализированных config/ модулей
- ✅ Auto-Generation - config.yaml генерируется автоматически
- ✅ Environment Variable Support - ${ENV_VAR} подстановка
- ✅ Type Safety - строгая валидация при загрузке
Modular Config Structure¶
7 Обязательных Модулей:¶
config/
├── network.yaml # Сервер, CORS, домены, сетевые настройки
├── database.yaml # PostgreSQL, пулы соединений
├── auth.yaml # JWT, Web3 auth, администраторы
├── blockchain.yaml # Сети, контракты, стейкинг
├── testing.yaml # Тестовые настройки
├── monitoring.yaml # Логирование, мониторинг, метрики
└── limits.yaml # Лимиты, стратегии, faucet
Generated Files:
config.yaml(read-only, chmod 444) - единая конфигурацияfrontend/shared/config/generated-config.ts(read-only) - TypeScript config.env.generated(read-only) - публичные env variables
Configuration Loading¶
Go Backend:¶
import "github.com/aiseeq/saga/backend/config"
func main() {
// Load unified configuration
cfg, err := config.LoadUnifiedConfig()
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
// Access configuration
serverPort := cfg.GetServerPort()
jwtSecret := cfg.GetJWTSecret()
dbURL := cfg.GetDatabaseURL()
// Use in services
jwtService := jwt_service.NewJWTService(cfg)
balanceService := balance_service.NewBalanceService(db, cfg)
}
Configuration Interface:¶
type UnifiedConfig interface {
// Server Configuration
GetServerHost() string
GetServerPort() int
GetAllowedOrigins() []string
// Database Configuration
GetDatabaseURL() string
GetDatabaseMaxConnections() int
GetDatabaseMaxIdleConnections() int
// JWT Configuration
GetJWTSecret() string
GetJWTExpirationHours() int
GetJWTIssuer() string
// Blockchain Configuration
GetBlockchainRPCURL() string
GetNetworkID() int
GetUSDCContractAddress() string
// Admin Management
IsAdmin(email string) bool
GetAdminWallets() []string
// Investment Strategies
GetInvestmentStrategy(id int) *InvestmentStrategy
GetAllInvestmentStrategies() []*InvestmentStrategy
// Faucet Configuration
GetFaucetAmountUSDC() SafeDecimal
GetFaucetCooldownHours() int
}
🛠️ Working with Config Modules¶
Adding New Configuration:¶
1. Choose Correct Module:
- Network settings →
config/network.yaml - Auth/JWT →
config/auth.yaml - Blockchain →
config/blockchain.yaml - Business limits →
config/limits.yaml
2. Edit Module File:
# config/limits.yaml
faucet:
amount_usdc: "1000.00"
cooldown_hours: 24
new_setting: "value" # Add here
3. Regenerate Config:
4. Add Getter Method:
// backend/config/unified_config_accessors.go
func (c *UnifiedConfig) GetFaucetNewSetting() string {
return c.Faucet.NewSetting
}
Example: Adding Email Configuration¶
Step 1 - Add to config/network.yaml:
Step 2 - Regenerate:
Step 3 - Add Interface Method:
// In UnifiedConfig interface
GetEmailSMTPHost() string
GetEmailSMTPPort() int
GetEmailFromAddress() string
// In implementation
func (c *UnifiedConfig) GetEmailSMTPHost() string {
return c.Email.SMTPHost
}
Step 4 - Use in Code:
func NewEmailService(cfg *config.UnifiedConfig) *EmailService {
return &EmailService{
smtpHost: cfg.GetEmailSMTPHost(),
smtpPort: cfg.GetEmailSMTPPort(),
from: cfg.GetEmailFromAddress(),
}
}
Environment Variables¶
Secure Secrets:¶
Principle: Secrets НЕ в config files, ТОЛЬКО в environment variables.
.env File (not committed):
# Database
DB_PASSWORD=secure-password
# JWT
JWT_SECRET=super-secret-minimum-32-characters-required
# Blockchain (optional for testnet)
HD_WALLET_MASTER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
# Email (if using)
SMTP_HOST=smtp.gmail.com
SMTP_PASSWORD=app-specific-password
Usage in Config:
# config/database.yaml
database:
host: "127.0.0.1"
port: 5432
user: "aisee"
password: "${DB_PASSWORD}" # From .env
database: "saga"
# config/auth.yaml
jwt:
secret: "${JWT_SECRET}" # From .env
expiration_hours: 24
.env Loading:¶
Automatic in UnifiedConfig:
// backend/config/unified_config_loader.go
func LoadUnifiedConfig() (*UnifiedConfig, error) {
// Auto-load .env file (using cleanenv library)
if err := godotenv.Load(); err != nil {
log.Warn("No .env file found, using environment variables only")
}
// Load config with env substitution
var cfg UnifiedConfig
if err := cleanenv.ReadConfig("config.yaml", &cfg); err != nil {
return nil, fmt.Errorf("failed to load config: %w", err)
}
// Validate
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("config validation failed: %w", err)
}
return &cfg, nil
}
Testing with UnifiedConfig¶
Test Configuration:¶
Example Test Configuration:
func LoadTestConfig() *config.UnifiedConfig {
// Load from test-specific config
cfg, err := config.LoadConfig("../../config.yaml")
if err != nil {
log.Fatalf("Failed to load test config: %v", err)
}
// Override for testing
cfg.Server.Port = 8081 // Different port for tests
cfg.Database.Name = "saga_test" // Test database
return cfg
}
Mock Config for Unit Tests:¶
type MockConfig struct {
ServerPort int
JWTSecret string
DatabaseURL string
AdminEmails []string
}
func (m *MockConfig) GetServerPort() int { return m.ServerPort }
func (m *MockConfig) GetJWTSecret() string { return m.JWTSecret }
// ... implement other interface methods
func TestServiceWithMockConfig(t *testing.T) {
mockCfg := &MockConfig{
JWTSecret: "test-secret",
AdminEmails: []string{"admin@test.com"},
}
service := NewService(mockCfg)
// ... test service
}
Configuration Validation¶
Automatic Validation:¶
// backend/config/unified_config_validator.go
func (c *UnifiedConfig) Validate() error {
var errs []string
// Validate server
if c.Server.Port < 1024 || c.Server.Port > 65535 {
errs = append(errs, "invalid server port")
}
// Validate JWT
if len(c.JWT.Secret) < 32 {
errs = append(errs, "JWT secret must be at least 32 characters")
}
// Validate database
if c.Database.MaxConnections < 1 {
errs = append(errs, "database max connections must be >= 1")
}
// Validate blockchain
if !strings.HasPrefix(c.Blockchain.USDC.ContractAddress, "0x") {
errs = append(errs, "invalid USDC contract address")
}
if len(errs) > 0 {
return fmt.Errorf("config validation failed: %s", strings.Join(errs, "; "))
}
return nil
}
Custom Validators:¶
func ValidateInvestmentStrategies(strategies []*InvestmentStrategy) error {
for _, s := range strategies {
if s.APY <= 0 || s.APY > 100 {
return fmt.Errorf("invalid APY for strategy %d: %f", s.ID, s.APY)
}
if !common.IsHexAddress(s.ContractAddress) {
return fmt.Errorf("invalid contract address for strategy %d", s.ID)
}
}
return nil
}
Config Generation System¶
Master Config Generator:¶
Location: tools/master-config-generator/
Generate All Configs:
What Happens:
- Reads all config/ modules (network.yaml, database.yaml, etc.)
- Validates module structure
- Generates unified config.yaml
- Generates frontend config files
- Applies read-only protection (chmod 444)
- Updates .env.generated with public constants
Hardcode Analyzer:¶
Prevent Hardcoded Constants:
Example Violation:
// ❌ WRONG (hardcode detected)
const ServerPort = 8080
// ✅ CORRECT (use UnifiedConfig)
port := cfg.GetServerPort()
Production Deployment¶
Environment-Specific Configs:¶
Development (.env.dev):
Staging (.env.staging):
Production (.env.production):
Deployment Checklist:¶
- Generate config:
make generate-config - Set production .env variables
- Validate config:
make validate-config - Test config loading in staging
- Deploy to production
- Verify config is loaded correctly
Related Documentation¶
- Environment Setup - Development environment
- Security Best Practices - Config security
📋 Метаданные¶
Версия: 2.4.82
Обновлено: 2025-10-21
Статус: Published