// Package config loads node-agent runtime configuration from environment variables. package config import ( "fmt" "os" "path/filepath" "strconv" ) // Config holds all runtime settings for the node agent. type Config struct { // NodeID is the UUID of this render node, registered in the orchestrator. // Must match a row in render.render_nodes. NodeID string // OrchestratorURL is the base URL of the V2 API gateway (internal network). // Example: http://gateway:8080 or http://172.30.0.5:8088 OrchestratorURL string // NodeHMACSecret is the shared secret sent as X-Node-Signature header. // Must match NODE_HMAC_SECRET in the render-svc environment. NodeHMACSecret string // Region is the datacenter/region label for this node (e.g. "iran-tehran-1"). // The orchestrator uses it to route region-preferred jobs to this node. Region string // AEPath is the full path to the aerender.exe binary. // Example: C:\Program Files\Adobe\Adobe After Effects 2024\Support Files\aerender.exe // Leave empty to use mock rendering (for development / testing without AE). AEPath string // AfterFxPath is the full path to afterfx.exe (the AE app, used to run the // template scanner script). Defaults to afterfx.exe alongside aerender.exe. // Leave AEPath empty too to disable scanning (dev/mock). AfterFxPath string // WorkDir is the scratch directory for render temp files and AE project copies. WorkDir string // HeartbeatIntervalSec is how often the agent sends a heartbeat to the orchestrator. HeartbeatIntervalSec int // PollIntervalSec is how long the agent waits between job-claim attempts when idle. PollIntervalSec int // AgentVersion is the semantic version string reported to the orchestrator. AgentVersion string // AEVersion is the After Effects version string reported to the orchestrator. // Example: "2024" AEVersion string // ListenPort is the port for the agent's own HTTP health endpoint. ListenPort int } // Load reads configuration from environment variables, returning an error // if any required variable is missing. func Load() (*Config, error) { c := &Config{ NodeID: os.Getenv("NODE_ID"), OrchestratorURL: getEnv("ORCHESTRATOR_URL", "http://localhost:8088"), NodeHMACSecret: getEnv("NODE_HMAC_SECRET", "node-secret-change-me"), Region: getEnv("NODE_REGION", ""), AEPath: getEnv("AE_PATH", ""), AfterFxPath: getEnv("AFTERFX_PATH", ""), WorkDir: getEnv("WORK_DIR", os.TempDir()), AgentVersion: getEnv("AGENT_VERSION", "0.1.0"), AEVersion: getEnv("AE_VERSION", "2024"), HeartbeatIntervalSec: getInt("HEARTBEAT_INTERVAL_SEC", 5), PollIntervalSec: getInt("POLL_INTERVAL_SEC", 3), ListenPort: getInt("LISTEN_PORT", 7777), } if c.NodeID == "" { return nil, fmt.Errorf("NODE_ID environment variable is required") } // Derive afterfx.exe next to aerender.exe when not explicitly set. if c.AfterFxPath == "" && c.AEPath != "" { c.AfterFxPath = filepath.Join(filepath.Dir(c.AEPath), "afterfx.exe") } return c, nil } func getEnv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func getInt(key string, fallback int) int { if v := os.Getenv(key); v != "" { if n, err := strconv.Atoi(v); err == nil { return n } } return fallback }