From 56b35ce66e8e4d2fa1af788db4f044281efe2651 Mon Sep 17 00:00:00 2001 From: eenblam Date: Sun, 14 Jul 2024 12:29:27 -0700 Subject: [PATCH] Refactor environment and config loading --- README.md | 6 +++--- config.go | 31 ++++++++++++++++++++++++++++++- main.go | 27 +++++++++++---------------- openai.go | 2 +- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 599d45e..3be8edc 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ obtaining mugshots, etc. Currently, I'm not publishing binaries for the project. You need: * A working Go installation -* An OpenAI API key set in the `OPENAI_API_KEY` environment variable. - * I store this in `.env`: `export OPENAI_API_KEY='ASDF'` +* An OpenAI API key set in the `JTT_OPENAI_API_KEY` environment variable. + * I store this in `.env`: `export JTT_OPENAI_API_KEY='ASDF'` * If you can roll a better text extraction model that we can run locally, please let me know! You can configure which jails to monitor and where to store data in `config.json`. For example, production data might be better stored in `/var/lib/jtt`, but the default is `./cache` for local development. @@ -50,4 +50,4 @@ with the current API. * Some jails (e.g. Harrison County, MS) require that you provide at least two characters in the "Last Name" field, otherwise no data will be provided. * Some jails don't consistently provide case or charge data. * Jails do not consistently use all the features of JailTracker. For example, there are some redundant date/time fields in the API, but often only one will be set. -* Most data seems to be input by hand, and is often quite messy. I'll try to document this on the repo's wiki at some point. \ No newline at end of file +* Most data seems to be input by hand, and is often quite messy. I'll try to document this on the repo's wiki at some point. diff --git a/config.go b/config.go index 1f79505..f6caa94 100644 --- a/config.go +++ b/config.go @@ -2,7 +2,9 @@ package main import ( "encoding/json" + "errors" "fmt" + "log" "os" ) @@ -24,7 +26,7 @@ type AppConfig struct { } // Marshal data from filename into provided config -func LoadConfig(config *AppConfig, filename string) error { +func (config *AppConfig) LoadConfig(filename string) error { file, err := os.ReadFile(filename) if err != nil { return fmt.Errorf("failed to read config file: %w", err) @@ -42,3 +44,30 @@ func LoadConfig(config *AppConfig, filename string) error { } return nil } + +type AppEnv struct { + OpenAIAPIKey string // "JTT_OPENAI_API_KEY" + // Directory to cache jail data + ConfigPath string // "JTT_CONFIG_PATH" +} + +// Load sets default values for empty optional environment variables +func (a *AppEnv) Load() { + a.OpenAIAPIKey = os.Getenv("JTT_OPENAI_API_KEY") + + a.ConfigPath = os.Getenv("JTT_CONFIG_PATH") + if a.ConfigPath == "" { + a.ConfigPath = "config.json" + } else { + log.Printf("Using config file from environment (JTT_CONFIG_PATH=\"%s\"", a.ConfigPath) + } +} + +// Validate checks that required environment variables are set. +// This is a separate step from Load, since it shouldn't be run on init() during tests. +func (a *AppEnv) ValidateRequired() error { + if a.OpenAIAPIKey == "" { + return errors.New("JTT_OPENAI_API_KEY must be set") + } + return nil +} diff --git a/main.go b/main.go index 07f1440..d3616d0 100644 --- a/main.go +++ b/main.go @@ -13,28 +13,23 @@ import ( var OpenAIAPIKey string -var Config = &AppConfig{} +var appConfig = &AppConfig{} +var appEnv = &AppEnv{} func init() { - OpenAIAPIKey = os.Getenv("OPENAI_API_KEY") - if OpenAIAPIKey == "" { - log.Fatal("OPENAI_API_KEY must be set") - } - - configPath := os.Getenv("JTT_CONFIG_DIR") - if configPath == "" { - configPath = "config.json" - } else { - log.Printf("Using config file from environment (JTT_CONFIG_DIR=\"%s\"", configPath) - } - err := LoadConfig(Config, configPath) + appEnv.Load() + err := appConfig.LoadConfig(appEnv.ConfigPath) if err != nil { - panic(err) + log.Fatalf("Failed to load config: %v", err) } } func main() { - for _, jailConfig := range Config.Jails { + err := appEnv.ValidateRequired() + if err != nil { + log.Fatalf("Failed to validate environment: %v", err) + } + for _, jailConfig := range appConfig.Jails { // Right now we do nothing here. Later, the cached data can be used to update a remote database. _, err := LoadJailCached(&jailConfig) if err != nil { @@ -91,5 +86,5 @@ func SaveJail(jail *Jail) error { func JailCachePath(jailName string) string { today := time.Now().Format("2006-01-02") filename := fmt.Sprintf("%s-%s.json", jailName, today) - return path.Join(Config.Cache, filename) + return path.Join(appConfig.Cache, filename) } diff --git a/openai.go b/openai.go index 4b6d8c4..f51513a 100644 --- a/openai.go +++ b/openai.go @@ -103,7 +103,7 @@ func solveCaptchaOpenAI(inline_image string) (string, error) { payload := getCaptchaPayload(inline_image) completion := &CompletionResponse{} headers := map[string][]string{ - "Authorization": {"Bearer " + OpenAIAPIKey}, + "Authorization": {"Bearer " + appEnv.OpenAIAPIKey}, } err := PostJSON[RequestPayload, CompletionResponse](OpenAICompletionsURL, headers, payload, completion) if err != nil {