package app import ( "errors" "os" "time" ) type Config struct { Environment, Address, PublicBaseURL, StudentPIN, InstructorPIN, SessionSecret, LogLevel, LogFormat string SessionDuration, ReadHeaderTimeout, ReadTimeout, WriteTimeout, IdleTimeout, ShutdownTimeout time.Duration } func LoadConfigFromEnv() (Config, error) { c := Config{Environment: get("APP_ENV", "development"), Address: get("HTTP_ADDRESS", ":8080"), PublicBaseURL: os.Getenv("PUBLIC_BASE_URL"), StudentPIN: os.Getenv("STUDENT_PIN"), InstructorPIN: os.Getenv("INSTRUCTOR_PIN"), SessionSecret: os.Getenv("SESSION_SIGNING_SECRET"), LogLevel: get("LOG_LEVEL", "info"), LogFormat: get("LOG_FORMAT", "text")} var err error c.SessionDuration, err = duration("SESSION_DURATION", "4h") if err != nil { return c, err } values := []struct { target *time.Duration key, fallback string }{{&c.ReadHeaderTimeout, "HTTP_READ_HEADER_TIMEOUT", "5s"}, {&c.ReadTimeout, "HTTP_READ_TIMEOUT", "30s"}, {&c.WriteTimeout, "HTTP_WRITE_TIMEOUT", "30s"}, {&c.IdleTimeout, "HTTP_IDLE_TIMEOUT", "60s"}, {&c.ShutdownTimeout, "HTTP_SHUTDOWN_TIMEOUT", "10s"}} for _, value := range values { *value.target, err = duration(value.key, value.fallback) if err != nil { return c, err } } if c.StudentPIN == "" || c.InstructorPIN == "" || len(c.SessionSecret) < 32 || c.PublicBaseURL == "" { return c, errors.New("STUDENT_PIN, INSTRUCTOR_PIN, PUBLIC_BASE_URL, and a 32+ character SESSION_SIGNING_SECRET are required") } return c, nil } func get(k, d string) string { if v := os.Getenv(k); v != "" { return v } return d } func duration(k, d string) (time.Duration, error) { return time.ParseDuration(get(k, d)) }