config.go raw

   1  package main
   2  
   3  import (
   4  	"os"
   5  	"time"
   6  
   7  	"go-simpler.org/env"
   8  	"next.orly.dev/pkg/lol/chk"
   9  	"next.orly.dev/pkg/lol/log"
  10  )
  11  
  12  // Config holds the configuration for the certificate manager.
  13  type Config struct {
  14  	// Domain is the wildcard domain to obtain a certificate for (e.g., "*.myapp.com")
  15  	Domain string `env:"ORLY_CERTS_DOMAIN" required:"true" usage:"wildcard domain (e.g., *.myapp.com)"`
  16  
  17  	// Email is the email address for the Let's Encrypt account
  18  	Email string `env:"ORLY_CERTS_EMAIL" required:"true" usage:"email for Let's Encrypt account"`
  19  
  20  	// DNSProvider is the name of the DNS provider (cloudflare, route53, hetzner, etc.)
  21  	DNSProvider string `env:"ORLY_CERTS_DNS_PROVIDER" required:"true" usage:"DNS provider name (cloudflare, route53, hetzner, etc.)"`
  22  
  23  	// OutputDir is the directory where certificates will be stored
  24  	OutputDir string `env:"ORLY_CERTS_OUTPUT_DIR" default:"/var/cache/orly-certs" usage:"certificate output directory"`
  25  
  26  	// RenewDays is the number of days before expiry to trigger renewal
  27  	RenewDays int `env:"ORLY_CERTS_RENEW_DAYS" default:"30" usage:"renew certificate when expiring within N days"`
  28  
  29  	// CheckInterval is how often to check for renewal
  30  	CheckInterval time.Duration `env:"ORLY_CERTS_CHECK_INTERVAL" default:"12h" usage:"how often to check for renewal"`
  31  
  32  	// ACMEServer is the ACME server URL (empty for production Let's Encrypt)
  33  	ACMEServer string `env:"ORLY_CERTS_ACME_SERVER" default:"" usage:"ACME server URL (empty for production)"`
  34  
  35  	// LogLevel is the log level
  36  	LogLevel string `env:"ORLY_CERTS_LOG_LEVEL" default:"info" usage:"log level (trace, debug, info, warn, error)"`
  37  
  38  	// AccountKeyPath is the path to store the ACME account private key
  39  	AccountKeyPath string `env:"ORLY_CERTS_ACCOUNT_KEY" default:"" usage:"path to ACME account key (auto-generated if empty)"`
  40  }
  41  
  42  // ProductionACMEServer is the Let's Encrypt production ACME server
  43  const ProductionACMEServer = "https://acme-v02.api.letsencrypt.org/directory"
  44  
  45  // StagingACMEServer is the Let's Encrypt staging ACME server (for testing)
  46  const StagingACMEServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
  47  
  48  // loadConfig loads configuration from environment variables.
  49  func loadConfig() *Config {
  50  	cfg := &Config{}
  51  	if err := env.Load(cfg, nil); chk.E(err) {
  52  		log.E.F("failed to load config: %v", err)
  53  		os.Exit(1)
  54  	}
  55  	return cfg
  56  }
  57  
  58  // ACMEServerURL returns the ACME server URL to use.
  59  func (c *Config) ACMEServerURL() string {
  60  	if c.ACMEServer != "" {
  61  		return c.ACMEServer
  62  	}
  63  	return ProductionACMEServer
  64  }
  65  
  66  // BaseDomain extracts the base domain from the wildcard domain.
  67  // e.g., "*.myapp.com" -> "myapp.com"
  68  func (c *Config) BaseDomain() string {
  69  	domain := c.Domain
  70  	if len(domain) > 2 && domain[:2] == "*." {
  71  		return domain[2:]
  72  	}
  73  	return domain
  74  }
  75