From 81cd07fc4279298a8da09d84af53869a0d6693b9 Mon Sep 17 00:00:00 2001 From: Atlantic Platform Group Date: Wed, 27 May 2026 19:56:13 -0400 Subject: [PATCH 1/3] fix(setup): detect and reuse existing postgres container Previously, running setup in a fresh clone or second worktree crashed with a Docker name conflict because oricms-postgres was already in use. Now the script checks for an existing container and reuses it if running, or removes it before starting fresh. Also detects whether docker compose (v2 plugin) or docker-compose (legacy) is available and uses the right command. --- scripts/setup.mjs | 69 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/scripts/setup.mjs b/scripts/setup.mjs index 46e8d98..bffea56 100755 --- a/scripts/setup.mjs +++ b/scripts/setup.mjs @@ -27,6 +27,16 @@ const args = new Set(process.argv.slice(2)); const SKIP_DOCKER = args.has("--skip-docker"); const RESET_DB = args.has("--reset-db"); +// Detect available docker compose command (v2 plugin vs legacy) +const DOCKER_COMPOSE = await (async () => { + try { + await run("docker", ["compose", "version"], { silent: true, ignoreError: false }); + return ["docker", "compose"]; + } catch { + return ["docker-compose"]; + } +})(); + function log(msg) { console.log(msg); } @@ -169,6 +179,37 @@ async function copyEnvFiles() { } } +function isValidHexKey(key, length) { + return typeof key === "string" && key.length === length && /^[0-9a-fA-F]+$/.test(key); +} + +async function validateEnvFiles() { + const rootEnvPath = ".env"; + const apiEnvPath = "packages/api/.env"; + + for (const envPath of [rootEnvPath, apiEnvPath]) { + if (!existsSync(envPath)) continue; + + const content = (await import("node:fs")).readFileSync(envPath, "utf-8"); + const lines = content.split("\n"); + const vars = {}; + for (const line of lines) { + const match = line.match(/^([A-Za-z0-9_]+)=(.*)$/); + if (match) vars[match[1]] = match[2]; + } + + const jwt = vars.JWT_SECRET; + if (!jwt || jwt.length < 32 || jwt.includes("change") || jwt.includes("your-")) { + warn(`${envPath}: JWT_SECRET is missing or looks like a placeholder`); + } + + const enc = vars.ENCRYPTION_KEY; + if (!isValidHexKey(enc, 64)) { + warn(`${envPath}: ENCRYPTION_KEY is missing or not a 64-character hex string`); + } + } +} + async function startPostgres() { if (SKIP_DOCKER) { info("Skipping Postgres startup (--skip-docker)"); @@ -177,6 +218,31 @@ async function startPostgres() { info("Starting Postgres via Docker..."); + // Check for existing container from a previous run or different worktree + try { + const { stdout } = await run( + "docker", + ["ps", "-a", "--filter", "name=^/oricms-postgres$", "--format", "{{.Names}}"], + { silent: true, ignoreError: true } + ); + if (stdout.trim() === "oricms-postgres") { + const { stdout: status } = await run( + "docker", + ["inspect", "--format", "{{.State.Status}}", "oricms-postgres"], + { silent: true } + ); + if (status.trim() === "running" && !RESET_DB) { + info("Existing oricms-postgres container is already running; reusing it"); + success("Postgres is ready"); + return; + } + info("Removing existing oricms-postgres container..."); + await run("docker", ["rm", "-f", "oricms-postgres"], { silent: true, ignoreError: true }); + } + } catch { + // ignore — proceed to normal startup + } + if (RESET_DB) { info("Removing existing database container (--reset-db)..."); try { @@ -186,7 +252,7 @@ async function startPostgres() { } } - await run("docker-compose", ["up", "-d", "postgres"]); + await run(DOCKER_COMPOSE[0], [...DOCKER_COMPOSE.slice(1), "up", "-d", "postgres"]); // Wait for Postgres to be ready info("Waiting for Postgres to be ready..."); @@ -258,6 +324,7 @@ async function main() { step(++currentStep, TOTAL_STEPS, "Copying environment files"); await copyEnvFiles(); + await validateEnvFiles(); step(++currentStep, TOTAL_STEPS, "Starting Postgres"); await startPostgres(); From cbd4b406205b271ebafb27192c6e1be63234316b Mon Sep 17 00:00:00 2001 From: Atlantic Platform Group Date: Wed, 27 May 2026 19:56:19 -0400 Subject: [PATCH 2/3] fix(env): align dev defaults and use obvious placeholders Both .env.example files now share consistent DATABASE_URL credentials (postgresql://oricms:oricms@localhost:5432/oricms) so a first-time user can connect to the Docker Postgres without editing the file. JWT_SECRET and ENCRYPTION_KEY use clearly fake placeholder strings (REPLACE_ME...) instead of values that look like they might be real. This makes it obvious when someone has forgotten to set them. Also removes the default RATE_LIMIT_REDIS_URL from the root example since Redis is not part of the local dev compose stack. --- .env.example | 6 +++--- packages/api/.env.example | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index a846aaf..2fda82d 100644 --- a/.env.example +++ b/.env.example @@ -10,7 +10,7 @@ DOCKER_ENV=false # API runtime / compose defaults DATABASE_URL=postgresql://oricms:oricms@localhost:5432/oricms -JWT_SECRET=change-me-with-at-least-32-characters +JWT_SECRET=REPLACE_ME_WITH_AT_LEAST_32_CHARACTERS GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= @@ -23,7 +23,7 @@ APP_BASE_URL=http://localhost:5173 TRUST_PROXY=1 # Production deployments should point this at a shared Redis instance so rate limits # remain consistent across multiple API instances. -RATE_LIMIT_REDIS_URL=redis://redis:6379/0 +RATE_LIMIT_REDIS_URL= RATE_LIMIT_REDIS_PREFIX=oricms:rate-limit # Encryption key used by the API for AES-256-GCM encryption of sensitive data @@ -32,4 +32,4 @@ RATE_LIMIT_REDIS_PREFIX=oricms:rate-limit # node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" # Keep packages/api/.env aligned with the API runtime values you actually use. # If you previously deployed with the old example key, rotate immediately. -ENCRYPTION_KEY= +ENCRYPTION_KEY=REPLACE_ME_64_HEX_CHARS_REQUIRED_FOR_DEVELOPMENT_ONLY diff --git a/packages/api/.env.example b/packages/api/.env.example index 99991f3..c2040fd 100644 --- a/packages/api/.env.example +++ b/packages/api/.env.example @@ -3,16 +3,16 @@ # Database # For Docker Compose: postgresql://oricms:oricms@postgres:5432/oricms -# For local development: postgresql://user:password@localhost:5432/oricms -DATABASE_URL=postgresql://localhost:5432/oricms +# For local development: postgresql://oricms:oricms@localhost:5432/oricms +DATABASE_URL=postgresql://oricms:oricms@localhost:5432/oricms # JWT Secret (min 32 characters) # Generate with: openssl rand -hex 32 -JWT_SECRET=your-jwt-secret-change-in-production +JWT_SECRET=REPLACE_ME_WITH_AT_LEAST_32_CHARACTERS # Encryption Key (32 bytes, 64 hex characters) # Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" -ENCRYPTION_KEY=your-encryption-key-change-in-production +ENCRYPTION_KEY=REPLACE_ME_64_HEX_CHARS_REQUIRED_FOR_DEVELOPMENT_ONLY # Allow creating sites with file:// repo URLs (development/testing only) # Keep unset or false in production. From cb5c0a8355e57717f044a2009f02bc2ce8c3d88b Mon Sep 17 00:00:00 2001 From: Atlantic Platform Group Date: Wed, 27 May 2026 19:56:24 -0400 Subject: [PATCH 3/3] docs: mention .nvmrc and nvm use in setup instructions The README and local-development guide now tell users to run nvm use before setup if they have nvm installed, matching the existing .nvmrc file that was already in the repo but not referenced in docs. --- README.md | 3 ++- docs/developer/local-development.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4a4306d..bd65f00 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ examples/ Runnable and copyable examples Requirements: -- Node.js 20+ +- Node.js 20+ (use `.nvmrc` if you use nvm) - npm 10+ - Docker - Git @@ -55,6 +55,7 @@ Requirements: Setup: ```bash +nvm use node scripts/setup.mjs ``` diff --git a/docs/developer/local-development.md b/docs/developer/local-development.md index 509a66a..a369d4d 100644 --- a/docs/developer/local-development.md +++ b/docs/developer/local-development.md @@ -4,7 +4,7 @@ Use this guide when you are setting up a new OriCMS worktree or returning to loc ## Runtime Requirements -- Node.js 20+ +- Node.js 20+ (use `.nvmrc` if you use nvm) - npm 10+ - Docker - Git