Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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=

Expand All @@ -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
Expand All @@ -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=<generate-64-char-hex-key>
ENCRYPTION_KEY=REPLACE_ME_64_HEX_CHARS_REQUIRED_FOR_DEVELOPMENT_ONLY
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,15 @@ examples/ Runnable and copyable examples

Requirements:

- Node.js 20+
- Node.js 20+ (use `.nvmrc` if you use nvm)
- npm 10+
- Docker
- Git

Setup:

```bash
nvm use
node scripts/setup.mjs
```

Expand Down
2 changes: 1 addition & 1 deletion docs/developer/local-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions packages/api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
69 changes: 68 additions & 1 deletion scripts/setup.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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)");
Expand All @@ -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 {
Expand All @@ -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...");
Expand Down Expand Up @@ -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();
Expand Down
Loading