- Removed YAML configuration files for the scraper and web commands, transitioning to an environment-based configuration system. - Updated configuration structs to utilize `env` tags for loading values from `.env` files, enhancing security and flexibility. - Implemented reflection-based parsing for environment variables, ensuring type-safe configuration loading. - Revised documentation to reflect the new configuration approach, including usage examples and best practices for environment variable naming conventions.
Environment-Based Configuration System
This package provides a configuration system for the Tender Management backend that loads configuration exclusively from .env files. It maintains the same structure as the previous YAML-based system while using environment variables for all configuration values.
Architecture
Base Configuration (BaseConfig)
The configuration system provides common configuration structs that are used across all commands:
- ServerConfig: HTTP server configuration (host, port, timeouts)
- DatabaseConfig: MongoDB connection configuration
- CacheConfig: Redis connection configuration
- LoggingConfig: Logging system configuration
- RateLimitConfig: Rate limiting configuration
Command-Specific Configuration
Each command can extend the base configuration by embedding the common configs and adding their own specific configuration fields.
Usage
1. Create Command-Specific Config
Create a config file for your command (e.g., cmd/yourcommand/config.go):
package main
import (
"time"
"tm/pkg/config"
)
// Config holds configuration for your command
type Config struct {
config.ServerConfig
config.DatabaseConfig
config.CacheConfig
config.LoggingConfig
YourSpecific YourSpecificConfig
}
type YourSpecificConfig struct {
SomeField string `env:"SOME_FIELD"`
SomeTimeout time.Duration `env:"SOME_TIMEOUT"`
SomeNumber int `env:"SOME_NUMBER"`
}
2. Load Configuration
In your command's main.go or bootstrap file:
func initConfig() Config {
conf, err := config.LoadConfig(".", &Config{})
if err != nil {
panic(fmt.Sprintf("Failed to load config: %v", err))
}
return *conf
}
3. Create .env Configuration File
Create a .env file in your command directory:
# Server Configuration
SERVER_HOST=localhost
SERVER_PORT=8080
SERVER_TIMEOUT=30s
SERVER_READ_TIMEOUT=10s
SERVER_WRITE_TIMEOUT=10s
# Database Configuration
MONGODB_URI=mongodb://localhost:27017
MONGODB_NAME=your_database
MONGODB_TIMEOUT=10s
MONGODB_MAX_POOL_SIZE=100
# Cache Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
REDIS_POOL_SIZE=10
# Logging Configuration
LOG_LEVEL=info
LOG_FORMAT=json
LOG_OUTPUT=stdout
LOG_FILE_PATH=/var/log/tm/your-command.log
LOG_FILE_MAX_SIZE=100
LOG_FILE_MAX_BACKUPS=5
LOG_FILE_MAX_AGE=30
LOG_FILE_COMPRESS=true
# Your command-specific configuration
SOME_FIELD=value
SOME_TIMEOUT=30s
SOME_NUMBER=42
Examples
Web Command Config
type Config struct {
config.ServerConfig
config.DatabaseConfig
config.CacheConfig
config.LoggingConfig
config.RateLimitConfig
UserAuth AuthConfig
CustomerAuth AuthConfig
Assets AssetsConfig
}
type AuthConfig struct {
JWT JWTConfig
}
type JWTConfig struct {
AccessSecret string `env:"USER_AUTH_ACCESS_SECRET"`
RefreshSecret string `env:"USER_AUTH_REFRESH_SECRET"`
AccessExpiresIn int `env:"USER_AUTH_ACCESS_EXPIRES_IN"`
RefreshExpiresIn int `env:"USER_AUTH_REFRESH_EXPIRES_IN"`
}
type AssetsConfig struct {
FlagsPath string `env:"ASSETS_FLAGS_PATH"`
}
Scraper Command Config
type Config struct {
config.DatabaseConfig
config.LoggingConfig
TED ScraperConfig
}
type ScraperConfig struct {
BaseURL string `env:"TED_BASE_URL"`
Timeout time.Duration `env:"TED_TIMEOUT"`
MaxRetries int `env:"TED_MAX_RETRIES"`
RetryDelay time.Duration `env:"TED_RETRY_DELAY"`
UserAgent string `env:"TED_USER_AGENT"`
MaxConcurrency int `env:"TED_MAX_CONCURRENCY"`
DownloadDir string `env:"TED_DOWNLOAD_DIR"`
CleanupAfter time.Duration `env:"TED_CLEANUP_AFTER"`
ScrapingInterval time.Duration `env:"TED_SCRAPING_INTERVAL"`
}
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 loads all values from .env files using the github.com/subosito/gotenv library.
Reflection-Based Parsing
The system uses reflection to automatically parse environment variables into struct fields based on the env tags.
Supported Types
The configuration system supports the following Go types:
stringint,int8,int16,int32,int64boolfloat32,float64time.Duration(parsed from strings like "30s", "1h", "5m")
Best Practices
-
Use descriptive environment variable names: Choose clear, descriptive names for your environment variables (e.g.,
MONGODB_URIinstead ofDB_URI). -
Use consistent naming conventions: Use uppercase with underscores for environment variable names (e.g.,
SERVER_HOST,LOG_LEVEL). -
Group related variables: Use prefixes to group related configuration (e.g.,
MONGODB_*,REDIS_*,LOG_*). -
Document your configuration: Add comments in your
.envfiles explaining what each variable does. -
Use appropriate types: Use
time.Durationfor durations, proper numeric types for numbers, etc. -
Provide sensible defaults: Consider providing sensible defaults in your code when environment variables are not set.
Migration from YAML System
If you're migrating from the old YAML-based configuration system:
- Remove
mapstructuretags from your config structs - Add
envtags to your config struct fields - Create
.envfiles instead ofconfig.yamlfiles - Update your
LoadConfigcalls to use the new system - Update function signatures to use the new config types
Example migration:
// Old way (YAML)
type Config struct {
Server config.ServerConfig `mapstructure:"server"`
Database config.DatabaseConfig `mapstructure:"database"`
}
// New way (.env)
type Config struct {
Server config.ServerConfig
Database config.DatabaseConfig
}
// Old way (YAML loading)
conf, err := config.LoadConfig(".", &Config{})
// New way (.env loading)
conf, err := config.LoadConfig(".", &Config{})
Environment Variable Naming Convention
The system follows these naming conventions:
- Server:
SERVER_*(e.g.,SERVER_HOST,SERVER_PORT) - Database:
MONGODB_*(e.g.,MONGODB_URI,MONGODB_NAME) - Cache:
REDIS_*(e.g.,REDIS_HOST,REDIS_PORT) - Logging:
LOG_*(e.g.,LOG_LEVEL,LOG_FORMAT) - Rate Limiting:
RATE_LIMIT_*(e.g.,RATE_LIMIT_REQUESTS_PER_MINUTE)
Error Handling
The configuration system provides detailed error messages when:
- The
.envfile cannot be loaded - Environment variables cannot be parsed into the expected types
- Required fields are missing (handled by your application logic)
Security Considerations
- Never commit
.envfiles: Add.envto your.gitignorefile - Use environment-specific files: Create different
.envfiles for different environments (.env.local,.env.production, etc.) - Validate sensitive values: Always validate sensitive configuration values like API keys and database URIs
- Use secure defaults: Provide secure default values for security-related configuration