v0
This commit is contained in:
275
internal/config/config.go
Normal file
275
internal/config/config.go
Normal file
@ -0,0 +1,275 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
// ConfigProvider defines the interface for configuration operations
|
||||
type ConfigProvider interface {
|
||||
// GetString retrieves a string configuration value
|
||||
GetString(key string) string
|
||||
|
||||
// GetInt retrieves an integer configuration value
|
||||
GetInt(key string) int
|
||||
|
||||
// GetBool retrieves a boolean configuration value
|
||||
GetBool(key string) bool
|
||||
|
||||
// GetDuration retrieves a duration configuration value
|
||||
GetDuration(key string) time.Duration
|
||||
|
||||
// GetStringSlice retrieves a string slice configuration value
|
||||
GetStringSlice(key string) []string
|
||||
|
||||
// IsSet checks if a configuration key is set
|
||||
IsSet(key string) bool
|
||||
|
||||
// Validate validates all required configuration values
|
||||
Validate() error
|
||||
|
||||
// GetDatabaseDSN constructs and returns the database connection string
|
||||
GetDatabaseDSN() string
|
||||
|
||||
// GetServerAddress returns the server address in host:port format
|
||||
GetServerAddress() string
|
||||
|
||||
// GetMetricsAddress returns the metrics server address in host:port format
|
||||
GetMetricsAddress() string
|
||||
|
||||
// IsDevelopment returns true if the environment is development
|
||||
IsDevelopment() bool
|
||||
|
||||
// IsProduction returns true if the environment is production
|
||||
IsProduction() bool
|
||||
}
|
||||
|
||||
// Config implements the ConfigProvider interface
|
||||
type Config struct {
|
||||
values map[string]string
|
||||
}
|
||||
|
||||
// NewConfig creates a new configuration provider
|
||||
func NewConfig() ConfigProvider {
|
||||
// Load .env file if it exists
|
||||
_ = godotenv.Load()
|
||||
|
||||
c := &Config{
|
||||
values: make(map[string]string),
|
||||
}
|
||||
|
||||
// Load environment variables
|
||||
for _, env := range os.Environ() {
|
||||
pair := strings.SplitN(env, "=", 2)
|
||||
if len(pair) == 2 {
|
||||
c.values[pair[0]] = pair[1]
|
||||
}
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
c.setDefaults()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Config) setDefaults() {
|
||||
defaults := map[string]string{
|
||||
"APP_NAME": "api-key-service",
|
||||
"APP_VERSION": "1.0.0",
|
||||
"SERVER_HOST": "0.0.0.0",
|
||||
"SERVER_PORT": "8080",
|
||||
"SERVER_READ_TIMEOUT": "30s",
|
||||
"SERVER_WRITE_TIMEOUT": "30s",
|
||||
"SERVER_IDLE_TIMEOUT": "120s",
|
||||
"DB_HOST": "localhost",
|
||||
"DB_PORT": "5432",
|
||||
"DB_NAME": "kms",
|
||||
"DB_USER": "postgres",
|
||||
"DB_PASSWORD": "postgres",
|
||||
"DB_SSLMODE": "disable",
|
||||
"DB_MAX_OPEN_CONNS": "25",
|
||||
"DB_MAX_IDLE_CONNS": "25",
|
||||
"DB_CONN_MAX_LIFETIME": "5m",
|
||||
"MIGRATION_PATH": "./migrations",
|
||||
"LOG_LEVEL": "info",
|
||||
"LOG_FORMAT": "json",
|
||||
"RATE_LIMIT_ENABLED": "true",
|
||||
"RATE_LIMIT_RPS": "100",
|
||||
"RATE_LIMIT_BURST": "200",
|
||||
"CACHE_ENABLED": "false",
|
||||
"CACHE_TTL": "1h",
|
||||
"JWT_ISSUER": "api-key-service",
|
||||
"AUTH_PROVIDER": "header", // header or sso
|
||||
"AUTH_HEADER_USER_EMAIL": "X-User-Email",
|
||||
"SSO_PROVIDER_URL": "",
|
||||
"SSO_CLIENT_ID": "",
|
||||
"SSO_CLIENT_SECRET": "",
|
||||
"INTERNAL_APP_ID": "internal.api-key-service",
|
||||
"INTERNAL_HMAC_KEY": "bootstrap-hmac-key-change-in-production",
|
||||
"METRICS_ENABLED": "false",
|
||||
"METRICS_PORT": "9090",
|
||||
}
|
||||
|
||||
for key, value := range defaults {
|
||||
if _, exists := c.values[key]; !exists {
|
||||
c.values[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetString retrieves a string configuration value
|
||||
func (c *Config) GetString(key string) string {
|
||||
return c.values[key]
|
||||
}
|
||||
|
||||
// GetInt retrieves an integer configuration value
|
||||
func (c *Config) GetInt(key string) int {
|
||||
if value, exists := c.values[key]; exists {
|
||||
if intVal, err := strconv.Atoi(value); err == nil {
|
||||
return intVal
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetBool retrieves a boolean configuration value
|
||||
func (c *Config) GetBool(key string) bool {
|
||||
if value, exists := c.values[key]; exists {
|
||||
if boolVal, err := strconv.ParseBool(value); err == nil {
|
||||
return boolVal
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetDuration retrieves a duration configuration value
|
||||
func (c *Config) GetDuration(key string) time.Duration {
|
||||
if value, exists := c.values[key]; exists {
|
||||
if duration, err := time.ParseDuration(value); err == nil {
|
||||
return duration
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetStringSlice retrieves a string slice configuration value
|
||||
func (c *Config) GetStringSlice(key string) []string {
|
||||
if value, exists := c.values[key]; exists {
|
||||
if value == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(value, ",")
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// IsSet checks if a configuration key is set
|
||||
func (c *Config) IsSet(key string) bool {
|
||||
_, exists := c.values[key]
|
||||
return exists
|
||||
}
|
||||
|
||||
// Validate validates all required configuration values
|
||||
func (c *Config) Validate() error {
|
||||
required := []string{
|
||||
"DB_HOST",
|
||||
"DB_PORT",
|
||||
"DB_NAME",
|
||||
"DB_USER",
|
||||
"DB_PASSWORD",
|
||||
"SERVER_HOST",
|
||||
"SERVER_PORT",
|
||||
"INTERNAL_APP_ID",
|
||||
"INTERNAL_HMAC_KEY",
|
||||
}
|
||||
|
||||
var missing []string
|
||||
for _, key := range required {
|
||||
if !c.IsSet(key) || c.GetString(key) == "" {
|
||||
missing = append(missing, key)
|
||||
}
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
return fmt.Errorf("missing required configuration keys: %s", strings.Join(missing, ", "))
|
||||
}
|
||||
|
||||
// Validate specific values
|
||||
if c.GetInt("DB_PORT") <= 0 || c.GetInt("DB_PORT") > 65535 {
|
||||
return fmt.Errorf("DB_PORT must be a valid port number")
|
||||
}
|
||||
|
||||
if c.GetInt("SERVER_PORT") <= 0 || c.GetInt("SERVER_PORT") > 65535 {
|
||||
return fmt.Errorf("SERVER_PORT must be a valid port number")
|
||||
}
|
||||
|
||||
if c.GetDuration("SERVER_READ_TIMEOUT") <= 0 {
|
||||
return fmt.Errorf("SERVER_READ_TIMEOUT must be a positive duration")
|
||||
}
|
||||
|
||||
if c.GetDuration("SERVER_WRITE_TIMEOUT") <= 0 {
|
||||
return fmt.Errorf("SERVER_WRITE_TIMEOUT must be a positive duration")
|
||||
}
|
||||
|
||||
if c.GetDuration("DB_CONN_MAX_LIFETIME") <= 0 {
|
||||
return fmt.Errorf("DB_CONN_MAX_LIFETIME must be a positive duration")
|
||||
}
|
||||
|
||||
authProvider := c.GetString("AUTH_PROVIDER")
|
||||
if authProvider != "header" && authProvider != "sso" {
|
||||
return fmt.Errorf("AUTH_PROVIDER must be either 'header' or 'sso'")
|
||||
}
|
||||
|
||||
if authProvider == "sso" {
|
||||
if c.GetString("SSO_PROVIDER_URL") == "" {
|
||||
return fmt.Errorf("SSO_PROVIDER_URL is required when AUTH_PROVIDER is 'sso'")
|
||||
}
|
||||
if c.GetString("SSO_CLIENT_ID") == "" {
|
||||
return fmt.Errorf("SSO_CLIENT_ID is required when AUTH_PROVIDER is 'sso'")
|
||||
}
|
||||
if c.GetString("SSO_CLIENT_SECRET") == "" {
|
||||
return fmt.Errorf("SSO_CLIENT_SECRET is required when AUTH_PROVIDER is 'sso'")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDatabaseDSN constructs and returns the database connection string
|
||||
func (c *Config) GetDatabaseDSN() string {
|
||||
return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
||||
c.GetString("DB_HOST"),
|
||||
c.GetInt("DB_PORT"),
|
||||
c.GetString("DB_USER"),
|
||||
c.GetString("DB_PASSWORD"),
|
||||
c.GetString("DB_NAME"),
|
||||
c.GetString("DB_SSLMODE"),
|
||||
)
|
||||
}
|
||||
|
||||
// GetServerAddress returns the server address in host:port format
|
||||
func (c *Config) GetServerAddress() string {
|
||||
return fmt.Sprintf("%s:%d", c.GetString("SERVER_HOST"), c.GetInt("SERVER_PORT"))
|
||||
}
|
||||
|
||||
// GetMetricsAddress returns the metrics server address in host:port format
|
||||
func (c *Config) GetMetricsAddress() string {
|
||||
return fmt.Sprintf("%s:%d", c.GetString("SERVER_HOST"), c.GetInt("METRICS_PORT"))
|
||||
}
|
||||
|
||||
// IsDevelopment returns true if the environment is development
|
||||
func (c *Config) IsDevelopment() bool {
|
||||
env := c.GetString("APP_ENV")
|
||||
return env == "development" || env == "dev" || env == ""
|
||||
}
|
||||
|
||||
// IsProduction returns true if the environment is production
|
||||
func (c *Config) IsProduction() bool {
|
||||
env := c.GetString("APP_ENV")
|
||||
return env == "production" || env == "prod"
|
||||
}
|
||||
Reference in New Issue
Block a user