123 lines
3.5 KiB
Go
123 lines
3.5 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
DatabaseDriverSQLite = "sqlite"
|
|
DatabaseDriverMySQL = "mysql"
|
|
|
|
DefaultDatabaseDriver = DatabaseDriverSQLite
|
|
DefaultDatabasePort = "3306"
|
|
DefaultDatabaseTLS = "false"
|
|
DefaultMaxOpenConns = "10"
|
|
DefaultMaxIdleConns = "5"
|
|
DefaultConnMaxLifetimeSeconds = "300"
|
|
EnvDatabaseDriver = "DB_DRIVER"
|
|
envDatabaseHost = "DB_HOST"
|
|
envDatabasePort = "DB_PORT"
|
|
envDatabaseUser = "DB_USER"
|
|
envDatabasePassword = "DB_PASSWORD"
|
|
envDatabaseName = "DB_NAME"
|
|
envDatabaseTLS = "DB_TLS"
|
|
envDatabaseMaxOpenConns = "DB_MAX_OPEN_CONNS"
|
|
envDatabaseMaxIdleConns = "DB_MAX_IDLE_CONNS"
|
|
envDatabaseConnMaxLifetimeSecs = "DB_CONN_MAX_LIFETIME_SECONDS"
|
|
)
|
|
|
|
var mysqlRequiredEnvVars = []string{envDatabaseHost, envDatabaseUser, envDatabasePassword, envDatabaseName}
|
|
|
|
var DatabaseTLSModes = []string{"false", "true", "skip-verify", "preferred"}
|
|
|
|
type DatabaseSettings struct {
|
|
Driver string
|
|
Host string
|
|
Port string
|
|
User string
|
|
Password string
|
|
Name string
|
|
TLS string
|
|
MaxOpenConns int
|
|
MaxIdleConns int
|
|
ConnMaxLifetime time.Duration
|
|
}
|
|
|
|
func DatabaseRequiredEnvVars(driver string) []string {
|
|
if driver == DatabaseDriverMySQL {
|
|
return slices.Clone(mysqlRequiredEnvVars)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func MissingEnvVarError(names []string, lookup func(string) (string, bool)) error {
|
|
for _, name := range names {
|
|
if _, isSet := lookup(name); !isSet {
|
|
return fmt.Errorf("Environment variable %s is not set", name)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func databaseDriverFromEnv() string {
|
|
return envOrDefault(EnvDatabaseDriver, DefaultDatabaseDriver)
|
|
}
|
|
|
|
func databaseSettingsFromEnv() (DatabaseSettings, error) {
|
|
driver := databaseDriverFromEnv()
|
|
if driver != DatabaseDriverMySQL {
|
|
return DatabaseSettings{Driver: driver}, nil
|
|
}
|
|
|
|
tls := envOrDefault(envDatabaseTLS, DefaultDatabaseTLS)
|
|
if !slices.Contains(DatabaseTLSModes, tls) {
|
|
return DatabaseSettings{}, fmt.Errorf("%s must be one of %s, got %q", envDatabaseTLS, strings.Join(DatabaseTLSModes, ", "), tls)
|
|
}
|
|
maxOpen, err := nonNegativeIntEnv(envDatabaseMaxOpenConns, DefaultMaxOpenConns)
|
|
if err != nil {
|
|
return DatabaseSettings{}, err
|
|
}
|
|
maxIdle, err := nonNegativeIntEnv(envDatabaseMaxIdleConns, DefaultMaxIdleConns)
|
|
if err != nil {
|
|
return DatabaseSettings{}, err
|
|
}
|
|
lifetimeSeconds, err := nonNegativeIntEnv(envDatabaseConnMaxLifetimeSecs, DefaultConnMaxLifetimeSeconds)
|
|
if err != nil {
|
|
return DatabaseSettings{}, err
|
|
}
|
|
|
|
return DatabaseSettings{
|
|
Driver: driver,
|
|
Host: os.Getenv(envDatabaseHost),
|
|
Port: envOrDefault(envDatabasePort, DefaultDatabasePort),
|
|
User: os.Getenv(envDatabaseUser),
|
|
Password: os.Getenv(envDatabasePassword),
|
|
Name: os.Getenv(envDatabaseName),
|
|
TLS: tls,
|
|
MaxOpenConns: maxOpen,
|
|
MaxIdleConns: maxIdle,
|
|
ConnMaxLifetime: time.Duration(lifetimeSeconds) * time.Second,
|
|
}, nil
|
|
}
|
|
|
|
func envOrDefault(name string, fallback string) string {
|
|
if value := os.Getenv(name); value != "" {
|
|
return value
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func nonNegativeIntEnv(name string, fallback string) (int, error) {
|
|
raw := envOrDefault(name, fallback)
|
|
value, err := strconv.Atoi(raw)
|
|
if err != nil || value < 0 {
|
|
return 0, fmt.Errorf("%s must be a non-negative whole number, got %q", name, raw)
|
|
}
|
|
return value, nil
|
|
}
|