Files
go-eve-pi/options.go

144 lines
3.8 KiB
Go

package main
import (
"fmt"
"os"
"reflect"
"strings"
logger "git.site.quack-lab.dev/dave/cylogger"
"github.com/joho/godotenv"
)
var options Options
func init() {
var err error
options, err = LoadOptions()
if err != nil {
logger.Error("Failed to load options %v", err)
return
}
}
type Options struct {
DBPath string `env:"DB_PATH" default:"eve-pi.db" description:"Database file path"`
Port string `env:"HTTP_SERVER_PORT" default:"3000" description:"HTTP server port"`
ClientID string `env:"ESI_CLIENT_ID" required:"true" description:"EVE SSO client ID"`
RedirectURI string `env:"ESI_REDIRECT_URI" required:"true" description:"EVE SSO redirect URI"`
Scopes []string `env:"ESI_SCOPES" default:"esi-planets.manage_planets.v1" description:"EVE SSO scopes (space-separated)"`
WebhookURL string `env:"WEBHOOK_URL" required:"true" description:"Webhook URL for notifications"`
WebhookEmail string `env:"WEBHOOK_EMAIL" required:"true" description:"Webhook authentication email"`
WebhookToken string `env:"WEBHOOK_TOKEN" required:"true" description:"Webhook authentication token"`
LogLevel string `env:"LOG_LEVEL" default:"info" description:"Logging level (debug, info, warning, error)"`
}
func LoadOptions() (Options, error) {
err := godotenv.Load()
if err != nil {
return Options{}, fmt.Errorf("failed to load environment variables: %w", err)
}
opts := Options{}
optsType := reflect.TypeOf(opts)
optsValue := reflect.ValueOf(&opts).Elem()
for i := 0; i < optsType.NumField(); i++ {
field := optsType.Field(i)
fieldValue := optsValue.Field(i)
envKey := field.Tag.Get("env")
if envKey == "" {
continue
}
envValue := os.Getenv(envKey)
required := field.Tag.Get("required") == "true"
defaultValue := field.Tag.Get("default")
if envValue == "" {
if required {
return Options{}, fmt.Errorf("required environment variable %s is not set", envKey)
}
if defaultValue != "" {
envValue = defaultValue
}
}
// Set the field value based on its type
if err := setFieldValue(fieldValue, envValue, field.Type); err != nil {
return Options{}, fmt.Errorf("invalid value for %s: %w", envKey, err)
}
}
return opts, nil
}
func setFieldValue(field reflect.Value, value string, fieldType reflect.Type) error {
switch fieldType.Kind() {
case reflect.String:
field.SetString(value)
case reflect.Slice:
if fieldType.Elem().Kind() == reflect.String {
// Handle []string by splitting on spaces
items := strings.Fields(value)
slice := reflect.MakeSlice(fieldType, len(items), len(items))
for i, item := range items {
slice.Index(i).SetString(item)
}
field.Set(slice)
}
default:
return fmt.Errorf("unsupported field type: %s", fieldType.Kind())
}
return nil
}
// GenerateEnvExample creates a .env.example file from the Options struct
func GenerateEnvExample() error {
optsType := reflect.TypeOf(Options{})
// Generate the .env.example content dynamically
content := "# EVE PI Configuration\n"
for i := 0; i < optsType.NumField(); i++ {
field := optsType.Field(i)
envKey := field.Tag.Get("env")
if envKey == "" {
continue
}
required := field.Tag.Get("required") == "true"
defaultValue := field.Tag.Get("default")
description := field.Tag.Get("description")
// Generate example value
exampleValue := generateExampleValue(field.Name, envKey, defaultValue, required)
content += fmt.Sprintf("# %s\n", description)
content += fmt.Sprintf("%s=%s\n\n", envKey, exampleValue)
}
file, err := os.Create(".env.example")
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(content)
return err
}
func generateExampleValue(fieldName, envKey, defaultValue string, required bool) string {
// If there's a default value, use it
if defaultValue != "" {
return defaultValue
}
// If no default value, use placeholder
return "your_" + strings.ToLower(envKey) + "_here"
}