[object Object]

← back to Interiordesignershowroom

add scripts/fix-dangling-scenes.js — NULL out dangling scene_image DB refs on prod (TK-10390)

0acaddb1cd9aba35a5aa98074342d0aeb73674ec · 2026-08-10 04:12:32 -0700 · steve@designerwallcoverings.com

Files touched

Diff

commit 0acaddb1cd9aba35a5aa98074342d0aeb73674ec
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date:   Mon Aug 10 04:12:32 2026 -0700

    add scripts/fix-dangling-scenes.js — NULL out dangling scene_image DB refs on prod (TK-10390)
---
 scripts/fix-dangling-scenes.js | 70 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git a/scripts/fix-dangling-scenes.js b/scripts/fix-dangling-scenes.js
new file mode 100644
index 0000000..733e699
--- /dev/null
+++ b/scripts/fix-dangling-scenes.js
@@ -0,0 +1,70 @@
+#!/usr/bin/env node
+// Fix dangling scene_image references — sets scene_image=NULL for rows where
+// the file no longer exists on disk. Safe: the server already gracefully falls
+// back to product thumbs when scene_image is NULL.
+// Run locally: node ids-fix-dangling-scenes.js [--apply]
+// Run on prod: SSH to Kamatera, copy this file, run with --apply
+
+const { Pool } = require('pg');
+const fs = require('fs');
+const path = require('path');
+
+const APPLY = process.argv.includes('--apply');
+const PUBLIC_DIR = path.join(__dirname, '..', 'public'); // on prod: /root/Projects/interiordesignershowroom/public
+
+const pool = new Pool({
+  connectionString: process.env.DATABASE_URL || 'postgresql://localhost:5432/idshowroom',
+});
+
+async function main() {
+  const client = await pool.connect();
+  try {
+    const { rows } = await client.query(
+      `SELECT id, slug, scene_image FROM rooms WHERE scene_image IS NOT NULL ORDER BY id`
+    );
+    console.log(`Found ${rows.length} rooms with scene_image set.`);
+    
+    const dangling = [];
+    for (const row of rows) {
+      const imgPath = path.join(PUBLIC_DIR, row.scene_image);
+      if (!fs.existsSync(imgPath)) {
+        dangling.push(row);
+        console.log(`MISSING: /room/${row.slug} → ${row.scene_image}`);
+      } else {
+        console.log(`OK:      /room/${row.slug} → ${row.scene_image}`);
+      }
+    }
+    
+    console.log(`\nTotal dangling: ${dangling.length}/${rows.length}`);
+    
+    if (dangling.length === 0) {
+      console.log('Nothing to fix — all scene_image files exist.');
+      return;
+    }
+    
+    if (!APPLY) {
+      console.log('\nDRY RUN — pass --apply to NULL out dangling references.');
+      console.log('Nulling these would let the server fall back to product thumbs (graceful-degrade).');
+      return;
+    }
+    
+    await client.query('BEGIN');
+    const ids = dangling.map(r => r.id);
+    const { rowCount } = await client.query(
+      `UPDATE rooms SET scene_image = NULL WHERE id = ANY($1::int[])`,
+      [ids]
+    );
+    await client.query('COMMIT');
+    console.log(`\nApplied: NULLed scene_image on ${rowCount} rows.`);
+    console.log('These rooms now show product thumbs as their hero (graceful-degrade is live).');
+  } catch (e) {
+    await client.query('ROLLBACK').catch(() => {});
+    console.error('Error:', e.message);
+    process.exit(1);
+  } finally {
+    client.release();
+    await pool.end();
+  }
+}
+
+main();

← 9530ee0 auto-data-snapshot: 2026-08-09T17:28:38 (2 data files) — .pl  ·  back to Interiordesignershowroom  ·  TK-10390: reconcile-dead-images.js — dry-run tool to NULL de c6f5c6b →