diff --git a/bridge-history-api/internal/config/config.go b/bridge-history-api/internal/config/config.go index a7f848a33..f26ec15f1 100644 --- a/bridge-history-api/internal/config/config.go +++ b/bridge-history-api/internal/config/config.go @@ -2,8 +2,12 @@ package config import ( "encoding/json" + "fmt" "os" "path/filepath" + "reflect" + "strconv" + "strings" "scroll-tech/common/database" ) @@ -12,7 +16,7 @@ import ( type FetcherConfig struct { Confirmation uint64 `json:"confirmation"` Endpoint string `json:"endpoint"` - StartHeight uint64 `json:"startHeight"` // Can only be configured to contract deployment height, message proof should be updated from the very beginning. + StartHeight uint64 `json:"startHeight"` BlockTime int64 `json:"blockTime"` FetchLimit uint64 `json:"fetchLimit"` MessengerAddr string `json:"MessengerAddr"` @@ -66,5 +70,96 @@ func NewConfig(file string) (*Config, error) { return nil, err } + // Override config with environment variables + err = overrideConfigWithEnv(cfg, "SCROLL_BRIDGE_HISTORY") + if err != nil { + return nil, err + } + return cfg, nil } + +// overrideConfigWithEnv recursively overrides config values with environment variables +func overrideConfigWithEnv(cfg interface{}, prefix string) error { + v := reflect.ValueOf(cfg) + if v.Kind() != reflect.Ptr || v.IsNil() { + return nil + } + v = v.Elem() + + t := v.Type() + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + fieldValue := v.Field(i) + + if !fieldValue.CanSet() { + continue + } + + tag := field.Tag.Get("json") + if tag == "" { + tag = strings.ToLower(field.Name) + } + + envKey := prefix + "_" + strings.ToUpper(tag) + + switch fieldValue.Kind() { + case reflect.Ptr: + if !fieldValue.IsNil() { + err := overrideConfigWithEnv(fieldValue.Interface(), envKey) + if err != nil { + return err + } + } + case reflect.Struct: + err := overrideConfigWithEnv(fieldValue.Addr().Interface(), envKey) + if err != nil { + return err + } + default: + if envValue, exists := os.LookupEnv(envKey); exists { + err := setField(fieldValue, envValue) + if err != nil { + return err + } + } + } + } + + return nil +} + +// setField sets the value of a field based on the environment variable value +func setField(field reflect.Value, value string) error { + switch field.Kind() { + case reflect.String: + field.SetString(value) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + intValue, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + field.SetInt(intValue) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + uintValue, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + field.SetUint(uintValue) + case reflect.Float32, reflect.Float64: + floatValue, err := strconv.ParseFloat(value, 64) + if err != nil { + return err + } + field.SetFloat(floatValue) + case reflect.Bool: + boolValue, err := strconv.ParseBool(value) + if err != nil { + return err + } + field.SetBool(boolValue) + default: + return fmt.Errorf("unsupported type: %v", field.Kind()) + } + return nil +} \ No newline at end of file diff --git a/coordinator/internal/config/config.go b/coordinator/internal/config/config.go index cbe9ca02d..23ca0b022 100644 --- a/coordinator/internal/config/config.go +++ b/coordinator/internal/config/config.go @@ -2,8 +2,12 @@ package config import ( "encoding/json" + "fmt" "os" "path/filepath" + "reflect" + "strconv" + "strings" "scroll-tech/common/database" ) @@ -72,5 +76,97 @@ func NewConfig(file string) (*Config, error) { return nil, err } + // Override config with environment variables + err = overrideConfigWithEnv(cfg, "SCROLL_COORDINATOR") + if err != nil { + return nil, err + } + return cfg, nil } + +// overrideConfigWithEnv recursively overrides config values with environment variables +func overrideConfigWithEnv(cfg interface{}, prefix string) error { + v := reflect.ValueOf(cfg) + if v.Kind() != reflect.Ptr || v.IsNil() { + return nil + } + v = v.Elem() + + t := v.Type() + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + fieldValue := v.Field(i) + + if !fieldValue.CanSet() { + continue + } + + tag := field.Tag.Get("json") + if tag == "" { + tag = strings.ToLower(field.Name) + } + + envKey := prefix + "_" + strings.ToUpper(tag) + + switch fieldValue.Kind() { + case reflect.Ptr: + if !fieldValue.IsNil() { + err := overrideConfigWithEnv(fieldValue.Interface(), envKey) + if err != nil { + return err + } + } + case reflect.Struct: + err := overrideConfigWithEnv(fieldValue.Addr().Interface(), envKey) + if err != nil { + return err + } + default: + if envValue, exists := os.LookupEnv(envKey); exists { + err := setField(fieldValue, envValue) + if err != nil { + return err + } + } + } + } + + return nil +} + +// setField sets the value of a field based on the environment variable value +func setField(field reflect.Value, value string) error { + switch field.Kind() { + case reflect.String: + field.SetString(value) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + intValue, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + field.SetInt(intValue) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + uintValue, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + field.SetUint(uintValue) + case reflect.Float32, reflect.Float64: + floatValue, err := strconv.ParseFloat(value, 64) + if err != nil { + return err + } + field.SetFloat(floatValue) + case reflect.Bool: + boolValue, err := strconv.ParseBool(value) + if err != nil { + return err + } + field.SetBool(boolValue) + default: + return fmt.Errorf("unsupported type: %v", field.Kind()) + } + return nil + +} \ No newline at end of file