Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
Squeamish about New()'s empty-domain-string sentinel for "dev mode, skip OIDC discovery" - split into New (always makes a real OIDC discovery call, all params required) and NewDev (no ctx/domain/credentials at all, since none are used). main.go now branches on cfg.DevAuthEnabled to pick the right constructor instead of main.go/config.go coordinating on when it's safe to pass empty strings. Also finishes out the dev-auth flow this enables: config.Load reads a DEV_AUTH_ENABLED-aware env file and only requires Auth0 vars when dev auth is off; a PORT config var replaces the hardcoded :8082; and the nav UI (layout/index templates, ui router) points login/logout links at /api/auth/dev-login and a new /api/auth/dev-logout route when dev auth is enabled, so the whole login/logout loop works locally without a real Auth0 app. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
129 lines
3.2 KiB
Go
129 lines
3.2 KiB
Go
// Package config loads application configuration from environment variables,
|
|
// optionally populated from a .env file in the working directory.
|
|
package config
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
type Config struct {
|
|
DatabaseURL string
|
|
|
|
Auth0Domain string
|
|
Auth0ClientID string
|
|
Auth0ClientSecret string
|
|
Auth0CallbackURL string
|
|
|
|
EtsyAPIKeystring string
|
|
EtsyAPISharedSecret string
|
|
|
|
// DevAuthEnabled, when true, exposes a route that mints a local
|
|
// login session for any user_id without going through Auth0. Must
|
|
// never be true outside local development.
|
|
DevAuthEnabled bool
|
|
|
|
Port int
|
|
}
|
|
|
|
var envFlagPtr = flag.String("env", "", "")
|
|
|
|
func init() {
|
|
flag.Parse()
|
|
}
|
|
|
|
// Load reads a .env file, if present, into the process environment, then
|
|
// reads the required configuration values from the environment.
|
|
func Load() (Config, error) {
|
|
dev, err := checkEnvFlag()
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
|
|
if dev {
|
|
if err := godotenv.Load(".env.dev"); err != nil && !os.IsNotExist(err) {
|
|
return Config{}, fmt.Errorf("failed to load .env.dev file: %w", err)
|
|
}
|
|
} else {
|
|
if err := godotenv.Load(); err != nil && !os.IsNotExist(err) {
|
|
return Config{}, fmt.Errorf("failed to load .env file: %w", err)
|
|
}
|
|
}
|
|
|
|
var (
|
|
cfg Config
|
|
missing []string
|
|
)
|
|
|
|
required := func(key string) string {
|
|
v := os.Getenv(key)
|
|
if v == "" {
|
|
missing = append(missing, key)
|
|
}
|
|
return v
|
|
}
|
|
|
|
cfg.DatabaseURL = required("DATABASE_URL")
|
|
|
|
// DEV_AUTH_ENABLED must be known before the Auth0 vars are read below,
|
|
// since it decides whether those are required at all.
|
|
if raw := os.Getenv("DEV_AUTH_ENABLED"); raw != "" {
|
|
devAuthEnabled, err := strconv.ParseBool(raw)
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("invalid DEV_AUTH_ENABLED value %q: %w", raw, err)
|
|
}
|
|
cfg.DevAuthEnabled = devAuthEnabled
|
|
}
|
|
|
|
// With DEV_AUTH_ENABLED, real Auth0 login/logout never runs (dev-login
|
|
// mints sessions locally instead), so these are unused and optional.
|
|
if cfg.DevAuthEnabled {
|
|
cfg.Auth0Domain = os.Getenv("AUTH0_DOMAIN")
|
|
cfg.Auth0ClientID = os.Getenv("AUTH0_CLIENT_ID")
|
|
cfg.Auth0ClientSecret = os.Getenv("AUTH0_CLIENT_SECRET")
|
|
cfg.Auth0CallbackURL = os.Getenv("AUTH0_CALLBACK_URL")
|
|
} else {
|
|
cfg.Auth0Domain = required("AUTH0_DOMAIN")
|
|
cfg.Auth0ClientID = required("AUTH0_CLIENT_ID")
|
|
cfg.Auth0ClientSecret = required("AUTH0_CLIENT_SECRET")
|
|
cfg.Auth0CallbackURL = required("AUTH0_CALLBACK_URL")
|
|
}
|
|
|
|
cfg.EtsyAPIKeystring = required("ETSY_API_KEYSTRING")
|
|
cfg.EtsyAPISharedSecret = required("ETSY_API_SHARED_SECRET")
|
|
|
|
portStr := required("PORT")
|
|
|
|
if len(missing) > 0 {
|
|
return Config{}, fmt.Errorf(
|
|
"missing required environment variables: %v (see .env.example)",
|
|
missing,
|
|
)
|
|
}
|
|
|
|
if port, err := strconv.Atoi(portStr); err != nil {
|
|
return Config{}, fmt.Errorf("invalid port number: %w", err)
|
|
} else {
|
|
cfg.Port = port
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func checkEnvFlag() (dev bool, err error) {
|
|
switch env := *envFlagPtr; env {
|
|
case "dev":
|
|
return true, nil
|
|
case "prd":
|
|
return false, nil
|
|
case "":
|
|
return false, fmt.Errorf("no env flag specified: must be one of dev, prd")
|
|
default:
|
|
return false, fmt.Errorf("invalid env flag specified: must be one of dev, prd: %q", env)
|
|
}
|
|
}
|