// Package config reads application settings from the environment. package config import ( "fmt" "os" "strconv" "strings" ) type Config struct { Addr string // listen address, e.g. ":8080" BaseDomain string // e.g. "example.com" (no port) PublicPort string // port to append to generated blog URLs ("" in prod behind a proxy) DatabaseURL string JWTSecret []byte // Bootstrap superadmin created on first start if no superadmin exists. SuperadminUsername string SuperadminPassword string MaxUploadBytes int64 // default per-file limit; the superadmin can override it per blog Dev bool // reload templates/static from disk } func Load() (*Config, error) { c := &Config{ Addr: env("ADDR", ":8080"), BaseDomain: strings.ToLower(strings.TrimSpace(env("BASE_DOMAIN", "blogspace.localhost"))), PublicPort: env("PUBLIC_PORT", ""), DatabaseURL: env("DATABASE_URL", "postgres://blogspace:blogspace@localhost:5432/blogspace?sslmode=disable"), JWTSecret: []byte(env("JWT_SECRET", "")), SuperadminUsername: env("SUPERADMIN_USERNAME", "admin"), SuperadminPassword: env("SUPERADMIN_PASSWORD", ""), Dev: envBool("DEV", false), } mb, err := strconv.Atoi(env("MAX_UPLOAD_MB", "10")) if err != nil || mb <= 0 { return nil, fmt.Errorf("MAX_UPLOAD_MB must be a positive integer") } c.MaxUploadBytes = int64(mb) << 20 if len(c.JWTSecret) == 0 { if !c.Dev { return nil, fmt.Errorf("JWT_SECRET must be set") } c.JWTSecret = []byte("dev-insecure-secret-change-me") } if len(c.JWTSecret) < 16 && !c.Dev { return nil, fmt.Errorf("JWT_SECRET must be at least 16 bytes") } if c.BaseDomain == "" { return nil, fmt.Errorf("BASE_DOMAIN must be set") } return c, nil } // RootSubdomain is the pseudo-subdomain of the blog served on the base domain // itself (and on www.). It is owned by the superadmin. const RootSubdomain = "www" // BlogURL returns the public URL for a blog subdomain. func (c *Config) BlogURL(sub string) string { if sub == RootSubdomain { return c.RootURL() } return "http://" + sub + "." + c.HostWithPort() } // RootURL returns the public URL of the management site. func (c *Config) RootURL() string { return "http://" + c.HostWithPort() } func (c *Config) HostWithPort() string { if c.PublicPort != "" { return c.BaseDomain + ":" + c.PublicPort } return c.BaseDomain } func env(key, def string) string { if v, ok := os.LookupEnv(key); ok { return v } return def } func envBool(key string, def bool) bool { v, ok := os.LookupEnv(key) if !ok { return def } switch strings.ToLower(v) { case "1", "true", "yes", "on": return true } return false }