← back to Wallco Ai
marketplace db: warn + fall through on non-URL DATABASE_URL; fix pm2 PORT to match nginx (9905); allow self-redeploy
912870e60f12f13f37b8565c514190999c05e6c3 · 2026-05-13 15:29:37 -0700 · SteveStudio2
Production prod DATABASE_URL was set to a bare DB name ('dw_unified') instead of a postgres URL, causing pg to DNS-resolve it as a hostname (ENOTFOUND base). marketplace/db.js now detects non-URLs, warns, and falls through to PGHOST/PGDATABASE explicit defaults (with dw_admin fallback on Linux).
ecosystem.config.js: PORT 9792 → 9905 to match the nginx proxy_pass target the deploy script established (Kamatera).
deploy-kamatera.sh: port pre-flight only fails on non-pm2-owned collisions, so re-deploys of the same service don't trip on themselves.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Files touched
M deploy-kamatera.shM ecosystem.config.jsM src/marketplace/db.js
Diff
commit 912870e60f12f13f37b8565c514190999c05e6c3
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 15:29:37 2026 -0700
marketplace db: warn + fall through on non-URL DATABASE_URL; fix pm2 PORT to match nginx (9905); allow self-redeploy
Production prod DATABASE_URL was set to a bare DB name ('dw_unified') instead of a postgres URL, causing pg to DNS-resolve it as a hostname (ENOTFOUND base). marketplace/db.js now detects non-URLs, warns, and falls through to PGHOST/PGDATABASE explicit defaults (with dw_admin fallback on Linux).
ecosystem.config.js: PORT 9792 → 9905 to match the nginx proxy_pass target the deploy script established (Kamatera).
deploy-kamatera.sh: port pre-flight only fails on non-pm2-owned collisions, so re-deploys of the same service don't trip on themselves.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---
deploy-kamatera.sh | 16 +++++++++++-----
ecosystem.config.js | 2 +-
src/marketplace/db.js | 20 ++++++++++++++++----
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/deploy-kamatera.sh b/deploy-kamatera.sh
index 2c75614..c8b4bb4 100755
--- a/deploy-kamatera.sh
+++ b/deploy-kamatera.sh
@@ -17,15 +17,21 @@ echo "Local: $LOCAL_DIR"
echo "Remote: $REMOTE:$REMOTE_DIR"
echo ""
-# 1. Port collision pre-flight
+# 1. Port collision pre-flight — only fail if a NON-pm2-managed process holds the port
echo "[1/6] Port pre-flight..."
COLLISION=$(ssh "$REMOTE" "ss -tln | grep ':${PORT} '" 2>/dev/null || true)
if [ -n "$COLLISION" ]; then
- echo "ERROR: Port $PORT already in use on Kamatera:"
- echo "$COLLISION"
- exit 1
+ PM2_OWNED=$(ssh "$REMOTE" "pm2 jlist 2>/dev/null | python3 -c 'import sys,json;procs=json.load(sys.stdin);hits=[p[\"name\"] for p in procs if str(p.get(\"pm2_env\",{}).get(\"PORT\",\"\"))==\"${PORT}\" or p.get(\"name\")==\"${PM2_NAME}\"];print(\",\".join(hits))' 2>/dev/null" || true)
+ if [ -n "$PM2_OWNED" ]; then
+ echo " Port $PORT held by pm2 process(es): $PM2_OWNED — will reload in step 4."
+ else
+ echo "ERROR: Port $PORT already in use on Kamatera (NOT by pm2):"
+ echo "$COLLISION"
+ exit 1
+ fi
+else
+ echo " Port $PORT is free."
fi
-echo " Port $PORT is free."
# 2. rsync (exclude heavy data dirs, .env, node_modules, git)
echo "[2/6] rsync..."
diff --git a/ecosystem.config.js b/ecosystem.config.js
index dd6a260..038a20a 100644
--- a/ecosystem.config.js
+++ b/ecosystem.config.js
@@ -5,7 +5,7 @@ module.exports = {
cwd: __dirname,
env: {
NODE_ENV: 'production',
- PORT: '9792',
+ PORT: '9905',
},
max_memory_restart: '500M',
error_file: 'logs/pm2.err.log',
diff --git a/src/marketplace/db.js b/src/marketplace/db.js
index 0c71c1a..9d78764 100644
--- a/src/marketplace/db.js
+++ b/src/marketplace/db.js
@@ -6,17 +6,29 @@ const { Pool } = require('pg');
// On Kamatera (linux) DATABASE_URL/MARKETPLACE_DB_URL with real creds is required.
// Prefer explicit MARKETPLACE_DB_URL; on darwin skip the dw_admin DATABASE_URL (which fails
// locally because that user only exists on prod) and fall through to trust auth.
+//
+// Defensive: only accept DATABASE_URL if it actually looks like a postgres URL.
+// Some boxes have DATABASE_URL set to just a database name (e.g. "dw_unified"),
+// which pg then tries to DNS-resolve as a hostname and we get ENOTFOUND.
const isDarwin = process.platform === 'darwin';
-const url = process.env.MARKETPLACE_DB_URL || (!isDarwin ? process.env.DATABASE_URL : null) || null;
+function looksLikeUrl(s) { return typeof s === 'string' && /^postgres(ql)?:\/\//i.test(s); }
+const rawUrl =
+ process.env.MARKETPLACE_DB_URL ||
+ (!isDarwin ? process.env.DATABASE_URL : null) ||
+ null;
+const url = looksLikeUrl(rawUrl) ? rawUrl : null;
+if (rawUrl && !url) {
+ console.warn('[marketplace.db] ignoring non-URL DATABASE_URL/MARKETPLACE_DB_URL ("%s") — falling through to PGHOST/PGDATABASE defaults', rawUrl);
+}
const pool = url
? new Pool({ connectionString: url, max: 10 })
: new Pool({
host: process.env.PGHOST || (isDarwin ? '/tmp' : '127.0.0.1'),
port: Number(process.env.PGPORT || 5432),
- database: process.env.PGDATABASE || 'dw_unified',
- user: process.env.PGUSER || process.env.USER || 'stevestudio2',
- password: process.env.PGPASSWORD || undefined,
+ database: process.env.PGDATABASE || (rawUrl && !looksLikeUrl(rawUrl) ? rawUrl : 'dw_unified'),
+ user: process.env.PGUSER || process.env.USER || (isDarwin ? 'stevestudio2' : 'dw_admin'),
+ password: process.env.PGPASSWORD || process.env.DW_UNIFIED_PG_PASSWORD || undefined,
max: 10,
});
← 69fa84a nav: surface Marketplace · Designers · Patterns in wallco.ai
·
back to Wallco Ai
·
nav: pin marketplace + wallco.ai menu bars to top of viewpor d9bcbb0 →