[object Object]

← back to 4square Admin

bulk endpoints: try/catch so settlement-trigger RAISE returns {blocked} not a crash

8e74e15fedaf3f7a542bfd8713ab89b29cce4cc2 · 2026-06-02 09:08:27 -0700 · SteveStudio2

publish/unpublish/delete now catch DB errors; publish of a settlement-BLOCKed design returns
{ok:false,blocked:true,error} cleanly (the trigger RAISE no longer crashes the request).
Lets the CNCP settlement-review action buttons surface the gate honestly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 8e74e15fedaf3f7a542bfd8713ab89b29cce4cc2
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Tue Jun 2 09:08:27 2026 -0700

    bulk endpoints: try/catch so settlement-trigger RAISE returns {blocked} not a crash
    
    publish/unpublish/delete now catch DB errors; publish of a settlement-BLOCKed design returns
    {ok:false,blocked:true,error} cleanly (the trigger RAISE no longer crashes the request).
    Lets the CNCP settlement-review action buttons surface the gate honestly.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 server.js | 37 ++++++++++++++++++++++++-------------
 1 file changed, 24 insertions(+), 13 deletions(-)

diff --git a/server.js b/server.js
index 2c90859..8bcc7d7 100644
--- a/server.js
+++ b/server.js
@@ -399,26 +399,37 @@ function parseIds(req, res) {
 
 app.post('/api/bulk/publish', async (req, res) => {
   const ids = parseIds(req, res); if (!ids) return;
-  const r = await pool.query(`UPDATE spoon_all_designs SET is_published=true WHERE id=ANY($1) RETURNING id`, [ids]);
-  res.json({ ok:true, action:'publish', changed: r.rows.length, ids: r.rows.map(x=>x.id) });
+  try {
+    const r = await pool.query(`UPDATE spoon_all_designs SET is_published=true WHERE id=ANY($1) RETURNING id`, [ids]);
+    res.json({ ok:true, action:'publish', changed: r.rows.length, ids: r.rows.map(x=>x.id) });
+  } catch (e) {
+    // The settlement DB trigger RAISEs on publish of a BLOCKed design (no
+    // allow_override here, intentionally). Return it cleanly instead of crashing.
+    const settlement = /settlement/i.test(e.message || '');
+    res.status(settlement ? 200 : 500).json({ ok:false, action:'publish', blocked: settlement, error: e.message });
+  }
 });
 app.post('/api/bulk/unpublish', async (req, res) => {
   const ids = parseIds(req, res); if (!ids) return;
-  const r = await pool.query(`UPDATE spoon_all_designs SET is_published=false WHERE id=ANY($1) RETURNING id`, [ids]);
-  res.json({ ok:true, action:'unpublish', changed: r.rows.length, ids: r.rows.map(x=>x.id) });
+  try {
+    const r = await pool.query(`UPDATE spoon_all_designs SET is_published=false WHERE id=ANY($1) RETURNING id`, [ids]);
+    res.json({ ok:true, action:'unpublish', changed: r.rows.length, ids: r.rows.map(x=>x.id) });
+  } catch (e) { res.status(500).json({ ok:false, action:'unpublish', error: e.message }); }
 });
 app.post('/api/bulk/delete', async (req, res) => {
   const ids = parseIds(req, res); if (!ids) return;
-  const { rows } = await pool.query(`SELECT id, local_path FROM spoon_all_designs WHERE id=ANY($1)`, [ids]);
-  const moved = [];
-  for (const r of rows) {
-    if (r.local_path && fs.existsSync(r.local_path)) {
-      const dest = path.join(TRASH, `${r.id}__${path.basename(r.local_path)}`);
-      try { fs.renameSync(r.local_path, dest); moved.push(dest); } catch {}
+  try {
+    const { rows } = await pool.query(`SELECT id, local_path FROM spoon_all_designs WHERE id=ANY($1)`, [ids]);
+    const moved = [];
+    for (const r of rows) {
+      if (r.local_path && fs.existsSync(r.local_path)) {
+        const dest = path.join(TRASH, `${r.id}__${path.basename(r.local_path)}`);
+        try { fs.renameSync(r.local_path, dest); moved.push(dest); } catch {}
+      }
     }
-  }
-  const d = await pool.query(`DELETE FROM spoon_all_designs WHERE id=ANY($1) RETURNING id`, [ids]);
-  res.json({ ok:true, action:'delete', deleted: d.rows.length, files_moved: moved.length, trash_dir: TRASH });
+    const d = await pool.query(`DELETE FROM spoon_all_designs WHERE id=ANY($1) RETURNING id`, [ids]);
+    res.json({ ok:true, action:'delete', deleted: d.rows.length, files_moved: moved.length, trash_dir: TRASH });
+  } catch (e) { res.status(500).json({ ok:false, action:'delete', error: e.message }); }
 });
 app.post('/api/bulk/tag', async (req, res) => {
   const ids = parseIds(req, res); if (!ids) return;

← 4732fba 4square admin: populate fliepaper-bugs matrix from fliepaper  ·  back to 4square Admin  ·  chore: macstudio3 migration — reconcile from mac2 + repoint d9f2548 →