[object Object]

← 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

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 →