Files
guestbook/config.go
T

107 lines
3.4 KiB
Go

package main
import (
"fmt"
"strings"
goutils "github.com/jkaninda/go-utils"
)
// Config is the runtime configuration, read entirely from environment
// variables — the twelve-factor way, and exactly how Miabi injects settings
// into a deployed application (Postgres connection details land here when you
// attach a managed database to the app).
type Config struct {
Port int
DBDriver string // "postgres" or "sqlite"
DatabaseDSN string
AppName string
Version string
Seed bool
// RedisURL enables the cross-replica broker; empty keeps it in-process.
RedisURL string
// AdminToken enables the /admin console; empty disables it.
AdminToken string
// DebugEndpoints exposes the /api/debug failure-injection endpoints.
DebugEndpoints bool
// TrustedProxies limits whose X-Forwarded-For is believed (CIDRs).
TrustedProxies []string
}
// LoadConfig builds the Config from the environment.
func LoadConfig() (Config, error) {
driver, dsn := resolveDatabase()
return Config{
Port: goutils.EnvInt("PORT", 8080),
AppName: goutils.Env("APP_NAME", "Miabi Guestbook"),
Version: resolveVersion(),
Seed: goutils.Env("SEED", "true") == "true",
DBDriver: driver,
DatabaseDSN: dsn,
RedisURL: goutils.Env("REDIS_URL", goutils.Env("REDIS_DATABASE_URL", "")),
AdminToken: goutils.Env("ADMIN_TOKEN", ""),
DebugEndpoints: goutils.Env("DEBUG_ENDPOINTS", "false") == "true",
TrustedProxies: splitList(goutils.Env("TRUSTED_PROXIES", "")),
}, nil
}
// resolveDatabase picks the database driver and DSN.
//
// The driver comes from DB_DRIVER (postgres|sqlite). When DB_DRIVER is unset it
// is inferred: if Postgres is configured (DATABASE_URL or DB_HOST is present)
// Postgres is used; otherwise the app falls back to a zero-config SQLite file —
// handy for local dev and demos with nothing else to set up. On Miabi, attach a
// managed Postgres and it is selected automatically.
func resolveDatabase() (driver, dsn string) {
driver = strings.ToLower(goutils.Env("DB_DRIVER", ""))
if driver == "" {
if goutils.Env("DATABASE_URL", "") != "" || goutils.Env("DB_HOST", "") != "" {
driver = "postgres"
} else {
driver = "sqlite"
}
}
switch driver {
case "sqlite", "sqlite3":
// A file path; use ":memory:" for an ephemeral in-memory database. The
// parent directory is created on startup. In the container this defaults
// to /data (a volume); see DB_PATH in the Dockerfile.
return "sqlite", goutils.Env("DB_PATH", "data/guestbook.db")
default:
return "postgres", postgresDSN()
}
}
// postgresDSN resolves the Postgres connection, following Miabi's convention:
//
// - DATABASE_URL — a full DSN (URL or key=value). Preferred, and what Miabi's
// managed databases inject. When set, it is used verbatim.
// - Discrete DB_* variables — assembled into a key=value DSN otherwise.
func postgresDSN() string {
if url := goutils.Env("DATABASE_URL", ""); url != "" {
return url
}
return fmt.Sprintf(
"host=%s user=%s password=%s dbname=%s port=%d sslmode=%s",
goutils.Env("DB_HOST", "localhost"),
goutils.Env("DB_USER", "postgres"),
goutils.Env("DB_PASSWORD", "postgres"),
goutils.Env("DB_NAME", "guestbook"),
goutils.EnvInt("DB_PORT", 5432),
goutils.Env("DB_SSL_MODE", "disable"),
)
}
func splitList(s string) []string {
var out []string
for _, p := range strings.Split(s, ",") {
if p = strings.TrimSpace(p); p != "" {
out = append(out, p)
}
}
return out
}