From 8cc10699fda0183aade5811eb413b23dfa9657ba Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Wed, 27 May 2026 17:58:23 +0530 Subject: [PATCH] Add environment variable support for cache engine configuration Extends config.loader.js to apply IFRAMELY_* env vars as a final override layer on top of file-based config, enabling container deployments to configure the cache engine and Redis connection without mounting a config.local.js file. Supported variables: IFRAMELY_CACHE_ENGINE redis | node-cache | memcached | no-cache IFRAMELY_CACHE_TTL cache TTL in seconds IFRAMELY_REDIS_HOST Redis hostname or IP IFRAMELY_REDIS_PORT Redis port IFRAMELY_REDIS_PASSWORD Redis auth password IFRAMELY_REDIS_TLS true to enable TLS socket IFRAMELY_REDIS_MODE standard | cluster Redis connection options are only applied when the effective cache engine resolves to redis (from env var or file config), preventing accidental REDIS_OPTIONS injection when a different engine is in use. Also updates docker-compose.yml with a Redis service for local testing, with a healthcheck and depends_on so iframely only starts once Redis is ready. Co-authored-by: Plane AI --- config.loader.js | 82 +++++++++++++++++++++++++++++++++++++++++++++- docker-compose.yml | 32 ++++++++++++++++-- 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/config.loader.js b/config.loader.js index e7bf4c1a4..c79c6a4b9 100644 --- a/config.loader.js +++ b/config.loader.js @@ -2,4 +2,84 @@ import iframelyConfig from './config.js'; // Load global config from exec dir, because `iframely` can be used as library. var globalConfig = await import(process.cwd() + '/config.js'); globalConfig = globalConfig && globalConfig.default; -export default {...iframelyConfig, ...globalConfig}; \ No newline at end of file + +// --------------------------------------------------------------------------- +// Environment variable overrides (Plane fork extension). +// +// These are applied as a final layer on top of config.js + config.local.js so +// that container deployments can configure iframely without baking secrets into +// a mounted JS file. +// +// Supported variables (all prefixed IFRAMELY_): +// +// Cache engine +// IFRAMELY_CACHE_ENGINE redis | node-cache | memcached | no-cache +// IFRAMELY_CACHE_TTL seconds (integer) +// +// Redis (standard mode, used when IFRAMELY_CACHE_ENGINE=redis) +// IFRAMELY_REDIS_HOST hostname or IP (default: 127.0.0.1) +// IFRAMELY_REDIS_PORT port number (default: 6379) +// IFRAMELY_REDIS_PASSWORD password (optional) +// IFRAMELY_REDIS_TLS true | false (enables TLS socket) +// IFRAMELY_REDIS_MODE standard | cluster (default: standard) +// --------------------------------------------------------------------------- + +var envOverrides = {}; + +// --- Cache engine --- +if (process.env.IFRAMELY_CACHE_ENGINE) { + envOverrides.CACHE_ENGINE = process.env.IFRAMELY_CACHE_ENGINE; +} + +if (process.env.IFRAMELY_CACHE_TTL) { + var ttl = parseInt(process.env.IFRAMELY_CACHE_TTL, 10); + if (!isNaN(ttl)) { + envOverrides.CACHE_TTL = ttl; + } +} + +// --- Redis mode --- +if (process.env.IFRAMELY_REDIS_MODE) { + envOverrides.REDIS_MODE = process.env.IFRAMELY_REDIS_MODE; +} + +// --- Redis connection options --- +// Resolve the effective cache engine (env var wins over file-based config). +var base = {...iframelyConfig, ...globalConfig}; +var effectiveEngine = process.env.IFRAMELY_CACHE_ENGINE || base.CACHE_ENGINE; + +if (effectiveEngine === 'redis' && ( + process.env.IFRAMELY_REDIS_HOST || + process.env.IFRAMELY_REDIS_PORT || + process.env.IFRAMELY_REDIS_PASSWORD || + process.env.IFRAMELY_REDIS_TLS)) { + var existingOptions = base.REDIS_OPTIONS || {}; + var existingSocket = existingOptions.socket || {}; + + var socketOverrides = {}; + if (process.env.IFRAMELY_REDIS_HOST) { + socketOverrides.host = process.env.IFRAMELY_REDIS_HOST; + } + if (process.env.IFRAMELY_REDIS_PORT) { + var port = parseInt(process.env.IFRAMELY_REDIS_PORT, 10); + if (!isNaN(port)) { + socketOverrides.port = port; + } + } + if (process.env.IFRAMELY_REDIS_TLS === 'true') { + socketOverrides.tls = true; + } + + var redisOptions = { + ...existingOptions, + socket: { ...existingSocket, ...socketOverrides }, + }; + + if (process.env.IFRAMELY_REDIS_PASSWORD) { + redisOptions.password = process.env.IFRAMELY_REDIS_PASSWORD; + } + + envOverrides.REDIS_OPTIONS = redisOptions; +} + +export default {...iframelyConfig, ...globalConfig, ...envOverrides}; diff --git a/docker-compose.yml b/docker-compose.yml index 616ba1099..2a3aaa8f3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,38 @@ version: "3" services: - api: + iframely: build: context: . dockerfile: Dockerfile - container_name: iframely + container_name: iframely restart: always ports: - "8061:8061" + environment: + IFRAMELY_CACHE_ENGINE: redis + IFRAMELY_CACHE_TTL: "86400" + IFRAMELY_REDIS_HOST: redis + IFRAMELY_REDIS_PORT: "6379" + # IFRAMELY_REDIS_PASSWORD: "" + # IFRAMELY_REDIS_TLS: "false" + # IFRAMELY_REDIS_MODE: standard + depends_on: + redis: + condition: service_healthy + + redis: + image: redis:7-alpine + container_name: iframely-redis + restart: always + # ports: + # - "6379:6379" + volumes: + - redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + +volumes: + redis-data: