111 lines
2.9 KiB
Go
111 lines
2.9 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
once sync.Once
|
|
base string // resolved data base dir
|
|
)
|
|
|
|
// DataDir returns the base directory for data (db.bolt, certs, logs, www, etc).
|
|
// Respects DATA_DIR env (absolute or relative). Falls back to "data" relative to CWD.
|
|
func DataDir() string {
|
|
once.Do(func() {
|
|
if d := os.Getenv("DATA_DIR"); d != "" {
|
|
base = d
|
|
return
|
|
}
|
|
// default relative to cwd
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
wd = "."
|
|
}
|
|
base = filepath.Join(wd, "data")
|
|
})
|
|
return base
|
|
}
|
|
|
|
// Resolve returns an absolute or relative path under the data dir for the given subpath (e.g. "db.bolt", "certs", "www").
|
|
// If the corresponding *_DIR env is set it is used as base instead.
|
|
func Resolve(sub string) string {
|
|
// Allow per-subdir override e.g. CERTS_DIR, WWW_DIR, LOGS_DIR, CHALLENGE_DIR
|
|
key := ""
|
|
switch sub {
|
|
case "db.bolt":
|
|
key = "DATA_DIR"
|
|
case "certs", "custom_ssl":
|
|
key = "CERTS_DIR"
|
|
case "logs":
|
|
key = "LOGS_DIR"
|
|
case "letsencrypt-acme-challenge", "challenge":
|
|
key = "CHALLENGE_DIR"
|
|
case "www":
|
|
if d := os.Getenv("WWW_DIR"); d != "" {
|
|
return filepath.Join(d, "") // allow override to point directly
|
|
}
|
|
key = "DATA_DIR"
|
|
case "html":
|
|
if d := os.Getenv("HTML_DIR"); d != "" {
|
|
return filepath.Join(d, "")
|
|
}
|
|
key = "DATA_DIR"
|
|
default:
|
|
key = "DATA_DIR"
|
|
}
|
|
|
|
dir := DataDir()
|
|
if key != "" {
|
|
if override := os.Getenv(key); override != "" {
|
|
dir = override
|
|
}
|
|
}
|
|
return filepath.Join(dir, sub)
|
|
}
|
|
|
|
// EnsureDataDirs creates the expected subdirs under the resolved data location (and www inside data by default).
|
|
// Also pre-creates the acme-challenge subdir layout that lego webroot + our handler expect.
|
|
func EnsureDataDirs() error {
|
|
dirs := []string{
|
|
Resolve(""),
|
|
Resolve("certs"),
|
|
Resolve("logs"),
|
|
Resolve("letsencrypt-acme-challenge"),
|
|
Resolve("www"), // data/www by default
|
|
}
|
|
for _, d := range dirs {
|
|
if err := os.MkdirAll(d, 0o755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// full layout for http-01 challenges written by lego
|
|
acme := filepath.Join(Resolve("letsencrypt-acme-challenge"), ".well-known", "acme-challenge")
|
|
if err := os.MkdirAll(acme, 0o755); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ResetForTest clears the cached data dir (sync.Once) so tests can change
|
|
// DATA_DIR / *_DIR envs via t.Setenv and observe fresh resolution.
|
|
// Call at the start of such tests. This is the supported way to make
|
|
// config testable without globals leaking across tests.
|
|
func ResetForTest() {
|
|
once = sync.Once{}
|
|
base = ""
|
|
}
|
|
|
|
// IsDevelopment reports whether PROXY_MODE=development (or "dev").
|
|
// Defaults to false (production mode).
|
|
// When true, all letsencrypt certs use self-signed test certs (via the cert manager's
|
|
// issueTestCert path). No domain tricks (.localtest etc.) are used. In production
|
|
// (default) real LE issuance is always attempted.
|
|
func IsDevelopment() bool {
|
|
mode := strings.ToLower(os.Getenv("PROXY_MODE"))
|
|
return mode == "development" || mode == "dev"
|
|
}
|