b7fa012d13
- Removed the old `infra.Config` structure and replaced it with a new modular configuration system in `pkg/config`. - Introduced a `Config` struct in `cmd/web/config.go` to hold command-specific configurations. - Updated configuration loading functions to utilize the new `LoadConfig` method with generics for type safety. - Adjusted various initialization functions in `cmd/web/bootstrap.go` and `cmd/web/main.go` to reflect the new configuration structure. - Cleaned up the `config.yaml` by removing obsolete fields and ensuring it aligns with the new configuration schema.
214 lines
5.4 KiB
Markdown
214 lines
5.4 KiB
Markdown
# Modular Configuration System
|
|
|
|
This package provides a modular configuration system for the Tender Management backend. It allows each command (web, scraper, worker, etc.) to have its own specific configuration while sharing common base configurations.
|
|
|
|
## Architecture
|
|
|
|
### Base Configuration (`BaseConfig`)
|
|
|
|
The `BaseConfig` struct contains common configuration fields that are used across all commands:
|
|
|
|
- **Server**: HTTP server configuration (host, port, timeouts)
|
|
- **Database**: MongoDB connection configuration
|
|
- **Cache**: Redis connection configuration
|
|
- **Logging**: Logging system configuration
|
|
|
|
### Command-Specific Configuration
|
|
|
|
Each command can extend the `BaseConfig` by embedding it and adding its own specific configuration fields.
|
|
|
|
## Usage
|
|
|
|
### 1. Create Command-Specific Config
|
|
|
|
Create a config file for your command (e.g., `cmd/yourcommand/config.go`):
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"time"
|
|
"tm/pkg/config"
|
|
)
|
|
|
|
// Config holds configuration for your command
|
|
type Config struct {
|
|
config.BaseConfig
|
|
YourSpecific YourSpecificConfig `mapstructure:"your_specific"`
|
|
}
|
|
|
|
type YourSpecificConfig struct {
|
|
SomeField string `mapstructure:"some_field"`
|
|
SomeTimeout time.Duration `mapstructure:"some_timeout"`
|
|
SomeNumber int `mapstructure:"some_number"`
|
|
}
|
|
```
|
|
|
|
### 2. Load Configuration
|
|
|
|
In your command's `main.go` or bootstrap file:
|
|
|
|
```go
|
|
func initConfig() Config {
|
|
conf, err := config.LoadConfig(".", &Config{})
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Failed to load config: %v", err))
|
|
}
|
|
|
|
return *conf
|
|
}
|
|
```
|
|
|
|
### 3. Create YAML Configuration File
|
|
|
|
Create a `config.yaml` file in your command directory:
|
|
|
|
```yaml
|
|
# Base configuration (common to all commands)
|
|
server:
|
|
host: "localhost"
|
|
port: 8080
|
|
timeout: 30s
|
|
read_timeout: 10s
|
|
write_timeout: 10s
|
|
|
|
database:
|
|
mongodb:
|
|
uri: "mongodb://localhost:27017"
|
|
name: "your_database"
|
|
timeout: 10s
|
|
max_pool_size: 100
|
|
|
|
cache:
|
|
redis:
|
|
host: "localhost"
|
|
port: 6379
|
|
password: ""
|
|
db: 0
|
|
pool_size: 10
|
|
|
|
logging:
|
|
level: "info"
|
|
format: "json"
|
|
output: "stdout"
|
|
file:
|
|
path: "/var/log/tm/your-command.log"
|
|
max_size: 100
|
|
max_backups: 5
|
|
max_age: 30
|
|
compress: true
|
|
|
|
# Your command-specific configuration
|
|
your_specific:
|
|
some_field: "value"
|
|
some_timeout: 30s
|
|
some_number: 42
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Web Command Config
|
|
|
|
```go
|
|
type Config struct {
|
|
config.BaseConfig
|
|
Queue QueueConfig `mapstructure:"queue"`
|
|
UserAuthorization AuthConfig `mapstructure:"user_authorization"`
|
|
CustomerAuthorization AuthConfig `mapstructure:"customer_authorization"`
|
|
AI AIConfig `mapstructure:"ai"`
|
|
RateLimit RateLimitConfig `mapstructure:"rate_limiting"`
|
|
}
|
|
```
|
|
|
|
### Scraper Command Config
|
|
|
|
```go
|
|
type Config struct {
|
|
config.BaseConfig
|
|
TED TEDConfig `mapstructure:"ted"`
|
|
}
|
|
|
|
type TEDConfig struct {
|
|
BaseURL string `mapstructure:"base_url"`
|
|
MaxRetries int `mapstructure:"max_retries"`
|
|
MaxConcurrency int `mapstructure:"max_concurrency"`
|
|
DownloadDir string `mapstructure:"download_dir"`
|
|
CleanupAfter time.Duration `mapstructure:"cleanup_after"`
|
|
}
|
|
```
|
|
|
|
### Worker Command Config
|
|
|
|
```go
|
|
type Config struct {
|
|
config.BaseConfig
|
|
Queue QueueConfig `mapstructure:"queue"`
|
|
AI AIConfig `mapstructure:"ai"`
|
|
Worker WorkerConfig `mapstructure:"worker"`
|
|
Scraping ScrapingConfig `mapstructure:"scraping"`
|
|
}
|
|
```
|
|
|
|
## Features
|
|
|
|
### Type Safety
|
|
|
|
The configuration loader uses Go generics to provide type-safe configuration loading. The function signature ensures that you get back exactly the type you expect.
|
|
|
|
### Environment Variable Support
|
|
|
|
The configuration system automatically supports environment variable overrides via Viper's `AutomaticEnv()` feature.
|
|
|
|
### YAML Support
|
|
|
|
Configuration files use YAML format for easy readability and maintenance.
|
|
|
|
### Validation
|
|
|
|
Each command can implement its own validation logic after loading the configuration.
|
|
|
|
## Best Practices
|
|
|
|
1. **Keep BaseConfig minimal**: Only add fields to `BaseConfig` that are truly common across all commands.
|
|
|
|
2. **Use descriptive names**: Choose clear, descriptive names for your configuration structs and fields.
|
|
|
|
3. **Document your config**: Add comments to your configuration structs explaining what each field does.
|
|
|
|
4. **Use appropriate types**: Use `time.Duration` for durations, proper numeric types for numbers, etc.
|
|
|
|
5. **Provide defaults**: Consider providing sensible defaults in your YAML files.
|
|
|
|
6. **Environment variables**: Use environment variables for sensitive data like API keys and passwords.
|
|
|
|
## Migration from Old System
|
|
|
|
If you're migrating from the old `infra.Config` system:
|
|
|
|
1. Remove imports of `tm/infra`
|
|
2. Add imports of `tm/pkg/config`
|
|
3. Change your config struct to embed `config.BaseConfig`
|
|
4. Update your `LoadConfig` call to use the new generic function
|
|
5. Update function signatures to use the new config types
|
|
|
|
Example migration:
|
|
|
|
```go
|
|
// Old way
|
|
func initConfig() infra.Config {
|
|
config, err := infra.LoadConfig(".")
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Failed to load config: %v", err))
|
|
}
|
|
return *config
|
|
}
|
|
|
|
// New way
|
|
func initConfig() Config {
|
|
conf, err := config.LoadConfig(".", &Config{})
|
|
if err != nil {
|
|
panic(fmt.Sprintf("Failed to load config: %v", err))
|
|
}
|
|
return *conf
|
|
}
|
|
``` |