package config import ( "encoding/json" "os" "strings" ) const defaultEnvPrefix = "GO_OMNI_" // ServerEnvAliases returns the documented short names for Config fields. // field is the JSON path of Config in a project (usually "server"), or "" // for Config itself. Canonical names win over aliases. Both use the same // EnvOptions.Empty/Lookup and typed parser; collections use JSON syntax. func ServerEnvAliases(prefix, field string) map[string][]string { root := prefix if field != "" { for _, part := range strings.Split(field, ".") { root += envName(part) + "_" } } names := map[string]string{ "ADDRESS": "HTTP_ADDR", "TRANSPORT": "TRANSPORT", "TLS_CERT_FILE": "TLS_CERT_FILE", "TLS_KEY_FILE": "TLS_KEY_FILE", "REQUEST_ID_HEADER": "REQUEST_ID_HEADER", "ENABLE_LOGGER": "ENABLE_LOGGER", "SLOW_REQUEST_THRESHOLD": "SLOW_REQUEST_THRESHOLD", "TRUSTED_PROXIES": "TRUSTED_PROXIES", "SHUTDOWN_TIMEOUT": "SHUTDOWN_TIMEOUT", "LOG_LEVEL": "LOG_LEVEL", "LOG_FORMAT": "LOG_FORMAT", "LOG_OUTPUT": "LOG_OUTPUT", "LOG_FILE": "LOG_FILE", "LOG_SERVICE": "SERVICE", "LOG_ENVIRONMENT": "ENVIRONMENT", "LOG_ADD_SOURCE": "LOG_ADD_SOURCE", "HTTP_READ_HEADER_TIMEOUT": "HTTP_READ_HEADER_TIMEOUT", "HTTP_READ_TIMEOUT": "HTTP_READ_TIMEOUT", "HTTP_WRITE_TIMEOUT": "HTTP_WRITE_TIMEOUT", "HTTP_IDLE_TIMEOUT": "HTTP_IDLE_TIMEOUT", "HTTP_MAX_HEADER_BYTES": "HTTP_MAX_HEADER_BYTES", "LIFECYCLE_STOP_TIMEOUT": "LIFECYCLE_STOP_TIMEOUT", "LIFECYCLE_DRAIN_TIMEOUT": "LIFECYCLE_DRAIN_TIMEOUT", "LIFECYCLE_CANCEL_TIMEOUT": "LIFECYCLE_CANCEL_TIMEOUT", } for _, endpoint := range []string{"LIVENESS", "READINESS", "OPENAPI"} { for _, part := range []string{"ENABLED", "PATH", "RESPONSE_MODE"} { if endpoint == "OPENAPI" && part == "RESPONSE_MODE" { continue } name := "ENDPOINTS_" + endpoint + "_" + part names[name] = name } } aliases := map[string][]string{} for key, alias := range names { if root+key != prefix+alias { aliases[root+key] = []string{prefix + alias} } } return aliases } // LoadFromEnv loads defaults using the legacy short-name conventions. func LoadFromEnv(prefix string) (Config, error) { return ApplyEnv(Default(), prefix) } // ApplyEnv retains legacy whitespace trimming, ignored empty variables and // comma-separated TRUSTED_PROXIES. New projects should use OverlayEnv with // ServerEnvAliases for one explicit empty-value policy across the whole config. func ApplyEnv(defaults Config, prefix string) (Config, error) { prefix = strings.TrimSpace(prefix) if prefix == "" { prefix = defaultEnvPrefix } lookup := func(key string) (string, bool) { raw, ok := os.LookupEnv(key) raw = strings.TrimSpace(raw) if ok && raw != "" && key == prefix+"TRUSTED_PROXIES" && !strings.HasPrefix(raw, "[") { values := strings.Split(raw, ",") for i := range values { values[i] = strings.TrimSpace(values[i]) } data, _ := json.Marshal(values) raw = string(data) } return raw, ok } defaults.Errors = defaults.Errors.Clone() defaults.TrustedProxies = append([]string(nil), defaults.TrustedProxies...) defaults.Log.RedactKeys = append([]string(nil), defaults.Log.RedactKeys...) cfg, err := OverlayEnv(defaults, EnvOptions{Prefix: prefix, Empty: "ignore", Lookup: lookup, Aliases: ServerEnvAliases(prefix, "")}) if err != nil { return Config{}, err } if err = cfg.Validate(); err != nil { return Config{}, err } return cfg, nil }