← 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 →