From 5f0a1872a5fa3368b3796400026eb33d1b909486 Mon Sep 17 00:00:00 2001 From: Achim Rohn Date: Mon, 1 Sep 2025 01:05:34 +0200 Subject: [PATCH] Add environment files --- starter/cli/generate_env.go | 80 ++++++++++++++++++++++++++++ starter/env/environment.go | 102 ++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 starter/cli/generate_env.go create mode 100644 starter/env/environment.go diff --git a/starter/cli/generate_env.go b/starter/cli/generate_env.go new file mode 100644 index 0000000..189a127 --- /dev/null +++ b/starter/cli/generate_env.go @@ -0,0 +1,80 @@ +package main + +import ( + "errors" + "ersteller-lib/starter/env" + "fmt" + "os" + "path/filepath" + "strings" +) + +// findRepoRoot walks up from the current working directory until it finds a go.mod file. +// It returns the directory containing go.mod, or an error if not found. +func findRepoRoot() (string, error) { + wd, err := os.Getwd() + if err != nil { + return "", err + } + curr := wd + for { + if _, err := os.Stat(filepath.Join(curr, "go.mod")); err == nil { + return curr, nil + } + parent := filepath.Dir(curr) + if parent == curr { + break + } + curr = parent + } + return "", errors.New("could not locate repository root (go.mod not found in any parent directory)") +} + +func main() { + root, err := findRepoRoot() + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + envPath := filepath.Join(root, ".env") + if _, err := os.Stat(envPath); err == nil { + fmt.Println(".env already exists at:", envPath) + fmt.Println("No changes made. Delete or move the existing .env to regenerate.") + return + } + + // Build .env content dynamically from keys exposed by env package + // so we don't duplicate the list here. + keys := env.EnvKeys() + + builder := &strings.Builder{} + fmt.Fprintln(builder, "# Environment configuration for Salezenify") + fmt.Fprintln(builder, "# Generated by cli/generate_env.go") + fmt.Fprintln(builder, "# Fill in the appropriate values for your environment.") + fmt.Fprintln(builder) + + // Optional defaults can be set here; leave blank for most keys to avoid assumptions. + defaults := map[string]string{ + "REPOSITORY_TYPE": "postgres", + } + + for _, k := range keys { + v := defaults[k] + if v == "" { + fmt.Fprintf(builder, "%s=\n", k) + } else { + fmt.Fprintf(builder, "%s=%s\n", k, v) + } + } + + content := builder.String() + + if err := os.WriteFile(envPath, []byte(content), 0600); err != nil { + fmt.Println("Failed to write .env:", err) + os.Exit(1) + } + + fmt.Println(".env file created at:", envPath) + fmt.Println("Review and update the values as needed.") +} diff --git a/starter/env/environment.go b/starter/env/environment.go new file mode 100644 index 0000000..918c6ae --- /dev/null +++ b/starter/env/environment.go @@ -0,0 +1,102 @@ +package env + +import ( + . "ersteller-lib" + "os" + + "github.com/joho/godotenv" +) + +const ( + EnvKeyRepositoryType = "REPOSITORY_TYPE" + EnvKeyDatabaseURL = "DATABASE_URL" + EnvKeySessionSecret = "SESSION_SECRET" + EnvKeyBaseURL = "BASE_URL" + EnvKeyIsLocal = "IS_LOCAL" + EnvKeyIsDev = "IS_DEV" + EnvKeyGoogleClientID = "GOOGLE_CLIENT_ID" + EnvKeyGoogleClientSecret = "GOOGLE_CLIENT_SECRET" + EnvKeyGoogleRedirectURL = "GOOGLE_REDIRECT_URL" + EnvKeyKeycloakDiscoveryURL = "KEYCLOAK_DISCOVERY_URL" + EnvKeyKeycloakClientID = "KEYCLOAK_CLIENT_ID" + EnvKeyKeycloakClientSecret = "KEYCLOAK_CLIENT_SECRET" +) + +type UrlApiKey struct { + Url string + ApiKey string +} + +type Environment struct { + DatabaseUrl string + MistralApiKey string + IsDev bool + RepositoryType string + GoogleSheetsCredPath string + GoogleSheetsSpreadId string + GoogleSheetsSheetName string + GoogleClientId string + GoogleClientSecret string + GoogleRedirectUrl string + IsLocal bool + SessionSecret string + ApifyKey string + Keycloak KeycloakConfig + BaseUrl string +} + +type KeycloakConfig struct { + DiscoveryUrl string + CLientId string + ClientSecret string +} + +func LoadEnvironment() Environment { + err := godotenv.Load() + if err != nil { + LogError("Error loading .env file: %v", err) + panic(err) + } + // Default repository type is postgres if not specified + repoType := os.Getenv(EnvKeyRepositoryType) + if repoType == "" { + repoType = "postgres" + } + + return Environment{ + DatabaseUrl: os.Getenv(EnvKeyDatabaseURL), + IsDev: os.Getenv(EnvKeyIsDev) == "true" || os.Getenv(EnvKeyIsLocal) == "true", + RepositoryType: repoType, + GoogleClientId: os.Getenv(EnvKeyGoogleClientID), + GoogleClientSecret: os.Getenv(EnvKeyGoogleClientSecret), + GoogleRedirectUrl: os.Getenv(EnvKeyGoogleRedirectURL), + IsLocal: os.Getenv(EnvKeyIsLocal) == "true", + SessionSecret: os.Getenv(EnvKeySessionSecret), + BaseUrl: os.Getenv(EnvKeyBaseURL), + Keycloak: KeycloakConfig{ + DiscoveryUrl: os.Getenv(EnvKeyKeycloakDiscoveryURL), + CLientId: os.Getenv(EnvKeyKeycloakClientID), + ClientSecret: os.Getenv(EnvKeyKeycloakClientSecret), + }, + } +} + +// EnvKeys returns all environment variable names used by the application configuration. +// This allows other packages (e.g., CLI tools) to discover the required keys +// without duplicating or hard-coding the list elsewhere. +func EnvKeys() []string { + return []string{ + EnvKeyRepositoryType, + EnvKeyDatabaseURL, + EnvKeySessionSecret, + EnvKeyBaseURL, + EnvKeyIsLocal, + EnvKeyIsDev, + EnvKeyGoogleClientID, + EnvKeyGoogleClientSecret, + EnvKeyGoogleRedirectURL, + EnvKeyKeycloakDiscoveryURL, + EnvKeyKeycloakClientID, + EnvKeyKeycloakClientSecret, + } +}