feat: load explicit configuration references and remove implicit env overlays
This commit is contained in:
@@ -5,17 +5,16 @@ package database
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Driver string `json:"driver"`
|
||||
DSN string `json:"-"`
|
||||
DSN string `json:"dsn"`
|
||||
MaxOpenConns int `json:"maxOpenConns"`
|
||||
MaxIdleConns int `json:"maxIdleConns"`
|
||||
ConnMaxLifetime time.Duration `json:"connMaxLifetime"`
|
||||
@@ -23,6 +22,14 @@ type Config struct {
|
||||
PingTimeout time.Duration `json:"pingTimeout"`
|
||||
}
|
||||
|
||||
// MarshalJSON omits the connection credential from serialized diagnostics.
|
||||
func (c Config) MarshalJSON() ([]byte, error) {
|
||||
type plain Config
|
||||
safe := plain(c)
|
||||
safe.DSN = ""
|
||||
return json.Marshal(safe)
|
||||
}
|
||||
|
||||
func DefaultConfig() Config { return DefaultPoolConfig().config() }
|
||||
|
||||
// PoolConfig returns driver-independent connection pool settings.
|
||||
@@ -34,49 +41,6 @@ func (c Config) PoolConfig() PoolConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigFromEnv reads GO_OMNI_DB_* style settings. DSN is required.
|
||||
func ConfigFromEnv(prefix string) (Config, error) {
|
||||
return ApplyEnv(DefaultConfig(), prefix)
|
||||
}
|
||||
|
||||
// ApplyEnv overlays nonempty environment settings on file/default configuration.
|
||||
// DSN is environment-only when Config is decoded from JSON or YAML.
|
||||
func ApplyEnv(cfg Config, prefix string) (Config, error) {
|
||||
prefix = strings.TrimSpace(prefix)
|
||||
if prefix == "" {
|
||||
prefix = "GO_OMNI_DB_"
|
||||
}
|
||||
if value := strings.TrimSpace(os.Getenv(prefix + "DRIVER")); value != "" {
|
||||
cfg.Driver = value
|
||||
}
|
||||
if cfg.Driver == "" {
|
||||
cfg.Driver = "mysql"
|
||||
}
|
||||
if value := strings.TrimSpace(os.Getenv(prefix + "DSN")); value != "" {
|
||||
cfg.DSN = value
|
||||
}
|
||||
if cfg.DSN == "" {
|
||||
return Config{}, fmt.Errorf("%sDSN is required", prefix)
|
||||
}
|
||||
var err error
|
||||
if cfg.MaxOpenConns, err = envInt(prefix+"MAX_OPEN_CONNS", cfg.MaxOpenConns); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
if cfg.MaxIdleConns, err = envInt(prefix+"MAX_IDLE_CONNS", cfg.MaxIdleConns); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
if cfg.ConnMaxLifetime, err = envDuration(prefix+"CONN_MAX_LIFETIME", cfg.ConnMaxLifetime); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
if cfg.ConnMaxIdleTime, err = envDuration(prefix+"CONN_MAX_IDLE_TIME", cfg.ConnMaxIdleTime); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
if cfg.PingTimeout, err = envDuration(prefix+"PING_TIMEOUT", cfg.PingTimeout); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
return cfg, cfg.Validate()
|
||||
}
|
||||
|
||||
func (c Config) Validate() error {
|
||||
if strings.TrimSpace(c.Driver) == "" {
|
||||
return errors.New("database driver is required")
|
||||
@@ -165,27 +129,3 @@ func (db *DB) InTx(ctx context.Context, options *sql.TxOptions, work func(*sql.T
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func envInt(key string, fallback int) (int, error) {
|
||||
value := strings.TrimSpace(os.Getenv(key))
|
||||
if value == "" {
|
||||
return fallback, nil
|
||||
}
|
||||
parsed, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s must be an integer: %w", key, err)
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func envDuration(key string, fallback time.Duration) (time.Duration, error) {
|
||||
value := strings.TrimSpace(os.Getenv(key))
|
||||
if value == "" {
|
||||
return fallback, nil
|
||||
}
|
||||
parsed, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s must be a duration: %w", key, err)
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user