diff --git a/internal/config/config.go b/internal/config/config.go index 60fb580..ac96fa0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -40,6 +40,13 @@ type Config struct { ZoomClientID string ZoomClientSecret string + // DataDir is where uploaded files (avatars, branding assets) are written. + // Defaults to the relative directory "data", which is what every existing + // deployment has always used; set DATA_DIR when the process runs somewhere + // its working directory is not writable, such as a read-only container image + // that mounts a volume elsewhere. + DataDir string + // CookieSecure sets the Secure flag on session cookies. Defaults to true // when BASE_URL starts with https://, but can be overridden explicitly via // COOKIE_SECURE=false for HTTPS-terminated-at-proxy setups where the binary @@ -89,6 +96,7 @@ func Load() *Config { ZoomClientSecret: getEnv("ZOOM_CLIENT_SECRET", ""), EmbedAllowedOrigins: splitCSV(getEnv("EMBED_ALLOWED_ORIGINS", "")), + DataDir: getEnv("DATA_DIR", "data"), } cfg.EncryptionKey = os.Getenv("CALNODE_ENCRYPTION_KEY") diff --git a/internal/config/config_test.go b/internal/config/config_test.go index a3a9962..990f1e4 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -121,3 +121,16 @@ func TestLoad_demoResetIntervalInvalidFallsBackToDefault(t *testing.T) { t.Errorf("DemoResetInterval = %v; want 30m default on invalid input", cfg.DemoResetInterval) } } + +// DATA_DIR moves the upload directory; unset, it is the relative "data" every +// existing deployment writes to, so nothing moves for anyone who never set it. +func TestLoad_dataDir(t *testing.T) { + t.Setenv("DATA_DIR", "") + if cfg := config.Load(); cfg.DataDir != "data" { + t.Errorf("DataDir default = %q; want data", cfg.DataDir) + } + t.Setenv("DATA_DIR", "/var/lib/calnode") + if cfg := config.Load(); cfg.DataDir != "/var/lib/calnode" { + t.Errorf("DataDir = %q; want /var/lib/calnode", cfg.DataDir) + } +} diff --git a/internal/server/server.go b/internal/server/server.go index d770024..d19e589 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -40,7 +40,14 @@ func BuildHandler(ctx context.Context, cfg *config.Config, db *sql.DB, logger *s h := handler.New(db, logger) h.SetBaseURL(cfg.BaseURL) h.SetPublicBaseURL(cfg.PublicBaseURL) - h.SetDataDir("data") + // DATA_DIR, defaulting to the relative "data" every deployment has always used. + // The fallback is repeated here because tests build a Config literal that skips + // Load, and an empty dir would put uploads beside the binary. + dataDir := cfg.DataDir + if dataDir == "" { + dataDir = "data" + } + h.SetDataDir(dataDir) h.SetEncKey(cfg.EncryptionKey) h.SetDemoMode(cfg.DemoMode) h.SetDemoResetInterval(cfg.DemoResetInterval)