Fix up options to be more better
This commit is contained in:
19
main.go
19
main.go
@@ -6,9 +6,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
logger "git.site.quack-lab.dev/dave/cylogger"
|
|
||||||
wh "go-eve-pi/webhook"
|
wh "go-eve-pi/webhook"
|
||||||
|
|
||||||
|
logger "git.site.quack-lab.dev/dave/cylogger"
|
||||||
|
|
||||||
"github.com/fasthttp/router"
|
"github.com/fasthttp/router"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
)
|
)
|
||||||
@@ -16,7 +17,19 @@ import (
|
|||||||
var webhook wh.Webhook
|
var webhook wh.Webhook
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Add flag for generating .env.example
|
||||||
|
help := flag.Bool("help", false, "Generate .env.example file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if *help {
|
||||||
|
if err := GenerateEnvExample(); err != nil {
|
||||||
|
logger.Error("Failed to generate .env.example: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
logger.Info("Generated .env.example file successfully")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
logger.InitFlag()
|
logger.InitFlag()
|
||||||
logger.Info("Starting Eve PI")
|
logger.Info("Starting Eve PI")
|
||||||
|
|
||||||
@@ -30,8 +43,8 @@ func main() {
|
|||||||
logger.Error("Failed to create SSO instance %v", err)
|
logger.Error("Failed to create SSO instance %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
webhook = wh.NewZulipWebhook(options.ZulipURL, options.ZulipEmail, options.ZulipToken)
|
webhook = wh.NewZulipWebhook(options.WebhookURL, options.WebhookEmail, options.WebhookToken)
|
||||||
|
|
||||||
// Setup fasthttp router
|
// Setup fasthttp router
|
||||||
r := router.New()
|
r := router.New()
|
||||||
|
|||||||
144
options.go
144
options.go
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
logger "git.site.quack-lab.dev/dave/cylogger"
|
logger "git.site.quack-lab.dev/dave/cylogger"
|
||||||
@@ -21,63 +22,122 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
DBPath string
|
DBPath string `env:"DB_PATH" default:"eve-pi.db" description:"Database file path"`
|
||||||
Port string
|
Port string `env:"HTTP_SERVER_PORT" default:"3000" description:"HTTP server port"`
|
||||||
|
|
||||||
ESIScopes []string
|
ClientID string `env:"ESI_CLIENT_ID" required:"true" description:"EVE SSO client ID"`
|
||||||
ESIRedirectURI string
|
RedirectURI string `env:"ESI_REDIRECT_URI" required:"true" description:"EVE SSO redirect URI"`
|
||||||
ESIClientID string
|
Scopes []string `env:"ESI_SCOPES" default:"esi-planets.manage_planets.v1" description:"EVE SSO scopes (space-separated)"`
|
||||||
|
|
||||||
WebhookURL string
|
WebhookURL string `env:"WEBHOOK_URL" required:"true" description:"Webhook URL for notifications"`
|
||||||
WebhookEmail string
|
WebhookEmail string `env:"WEBHOOK_EMAIL" required:"true" description:"Webhook authentication email"`
|
||||||
WebhookToken string
|
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) {
|
func LoadOptions() (Options, error) {
|
||||||
// Load environment variables strictly from .env file (fail if there's an error loading)
|
err := godotenv.Load()
|
||||||
if err := godotenv.Load(); err != nil {
|
if err != nil {
|
||||||
return Options{}, fmt.Errorf("error loading .env file: %w", err)
|
return Options{}, fmt.Errorf("failed to load environment variables: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dbPath := getOrDefault("DB_PATH", "eve-pi.db")
|
opts := Options{}
|
||||||
port := getOrDefault("HTTP_SERVER_PORT", "3000")
|
optsType := reflect.TypeOf(opts)
|
||||||
|
optsValue := reflect.ValueOf(&opts).Elem()
|
||||||
|
|
||||||
clientID := getOrFatal("ESI_CLIENT_ID")
|
for i := 0; i < optsType.NumField(); i++ {
|
||||||
redirectURI := getOrFatal("ESI_REDIRECT_URI")
|
field := optsType.Field(i)
|
||||||
rawScopes := getOrDefault("ESI_SCOPES", "esi-planets.manage_planets.v1")
|
fieldValue := optsValue.Field(i)
|
||||||
scopes := strings.Fields(rawScopes)
|
|
||||||
|
|
||||||
webhookUrl := getOrFatal("WEBHOOK_URL")
|
envKey := field.Tag.Get("env")
|
||||||
webhookEmail := getOrFatal("WEBHOOK_EMAIL")
|
if envKey == "" {
|
||||||
webhookToken := getOrFatal("WEBHOOK_TOKEN")
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
return Options{
|
envValue := os.Getenv(envKey)
|
||||||
DBPath: dbPath,
|
required := field.Tag.Get("required") == "true"
|
||||||
Port: port,
|
defaultValue := field.Tag.Get("default")
|
||||||
|
|
||||||
ESIClientID: clientID,
|
if envValue == "" {
|
||||||
ESIRedirectURI: redirectURI,
|
if required {
|
||||||
ESIScopes: scopes,
|
return Options{}, fmt.Errorf("required environment variable %s is not set", envKey)
|
||||||
|
}
|
||||||
|
if defaultValue != "" {
|
||||||
|
envValue = defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WebhookURL: webhookUrl,
|
// Set the field value based on its type
|
||||||
WebhookEmail: webhookEmail,
|
if err := setFieldValue(fieldValue, envValue, field.Type); err != nil {
|
||||||
WebhookToken: webhookToken,
|
return Options{}, fmt.Errorf("invalid value for %s: %w", envKey, err)
|
||||||
}, nil
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func getOrFatal(key string) string {
|
|
||||||
value := os.Getenv(key)
|
|
||||||
if value == "" {
|
|
||||||
logger.Error("Environment variable %s is required", key)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
return value
|
|
||||||
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrDefault(key, defaultValue string) string {
|
func setFieldValue(field reflect.Value, value string, fieldType reflect.Type) error {
|
||||||
value := os.Getenv(key)
|
switch fieldType.Kind() {
|
||||||
if value == "" {
|
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
|
return defaultValue
|
||||||
}
|
}
|
||||||
return value
|
|
||||||
|
// If no default value, use placeholder
|
||||||
|
return "your_" + strings.ToLower(envKey) + "_here"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user