package main import ( "context" "encoding/json" "errors" "fmt" "log" "os" "regexp" "strconv" "strings" "time" ) type Config struct { Port int Debug bool InitializeSlack bool SlackToken string SlackChannels []string DriverConn string BasicAuthUser string BasicAuthPassword string FillWithTestdata bool OllamaURL string OllamaModel string AssetPattern string DatacenterPattern string EventNamePattern string driver Driver storage Storage ai AI slackToModelPipeline Pipeline modelToPersistencePipeline Pipeline } var ( renderAssetPattern = `(dpg|svc|red)-[a-z0-9-]*[a-z0-9]|ip-[0-9]+-[0-9]+-[0-9]+-[0-9]+\.[a-z]+-[a-z]+-[0-9]+\.compute\.internal` renderDatacenterPattern = `[a-z]{4}[a-z]*-[0-9]` renderEventNamePattern = `(\[[^\]]*\] *)?(?P.*)` ) func newConfig(ctx context.Context) (Config, error) { return newConfigFromEnv(ctx, os.Getenv) } func newConfigFromEnv(ctx context.Context, getEnv func(string) string) (Config, error) { def := Config{ Port: 38080, OllamaModel: "gemma:2b", AssetPattern: renderAssetPattern, DatacenterPattern: renderDatacenterPattern, EventNamePattern: renderEventNamePattern, } var m map[string]any if b, err := json.Marshal(def); err != nil { return Config{}, err } else if err := json.Unmarshal(b, &m); err != nil { return Config{}, err } re := regexp.MustCompile(`[A-Z]`) for k, v := range m { envK := k idxes := re.FindAllIndex([]byte(envK), -1) for i := len(idxes) - 1; i >= 0; i-- { idx := idxes[i] if idx[0] > 0 { envK = fmt.Sprintf("%s_%s", envK[:idx[0]], envK[idx[0]:]) } } envK = strings.ToUpper(envK) s := getEnv(envK) if s == "" { continue } switch v.(type) { case string: m[k] = s case int64, float64: n, err := strconv.ParseFloat(s, 32) if err != nil { return Config{}, err } m[k] = n case bool: got, err := strconv.ParseBool(s) if err != nil { return Config{}, err } m[k] = got case nil, []interface{}: m[k] = strings.Split(s, ",") default: return Config{}, fmt.Errorf("not impl: parse %s as %T", envK, v) } } var result Config if b, err := json.Marshal(m); err != nil { return Config{}, err } else if err := json.Unmarshal(b, &result); err != nil { return Config{}, err } ctx, can := context.WithTimeout(ctx, time.Minute) defer can() driver, err := NewDriver(ctx, result.DriverConn) if err != nil { return Config{}, err } result.driver = driver if !result.FillWithTestdata { //} else if err := result.driver.FillWithTestdata(ctx, result.AssetPattern, result.DatacenterPattern, result.EventNamePattern); err != nil { } else { return Config{}, errors.New("not impl") } if result.Debug { log.Printf("connected to driver at %s (%s @%s)", result.DriverConn, result.driver.engine, result.driver.conn) } storage, err := NewStorage(ctx, result.driver) if err != nil { return Config{}, err } result.storage = storage if result.OllamaURL != "" { result.ai = NewAIOllama(result.OllamaURL, result.OllamaModel) } else { result.ai = NewAINoop() } slackToModelPipeline, err := NewSlackToModelPipeline(ctx, result) if err != nil { return Config{}, err } result.slackToModelPipeline = slackToModelPipeline modelToPersistencePipeline, err := NewModelToPersistencePipeline(ctx, result) if err != nil { return Config{}, err } result.modelToPersistencePipeline = modelToPersistencePipeline return result, nil }