← back to Patterndesignlab
Exclude settlement vision_blocked/vision_review designs from storefront (designs/facets/detail/designer); surface status in admin for audit
df1617fcaa0dca8c4b867f1e5af34498adfb06e3 · 2026-07-04 13:40:23 -0700 · Steve
Files touched
Diff
commit df1617fcaa0dca8c4b867f1e5af34498adfb06e3
Author: Steve <steve@designerwallcoverings.com>
Date: Sat Jul 4 13:40:23 2026 -0700
Exclude settlement vision_blocked/vision_review designs from storefront (designs/facets/detail/designer); surface status in admin for audit
---
server.js | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/server.js b/server.js
index e69760b..dc43625 100644
--- a/server.js
+++ b/server.js
@@ -44,6 +44,13 @@ app.get('/api/config', (req, res) => res.json({
note: stripe ? 'TEST MODE — no real charges' : 'test keys not yet installed — inquiries only',
}));
+// ---- served-visibility gate ----
+// The settlement post-gen VISION pass flags designs whose local master image fails the
+// settlement gate. 'vision_blocked' (full Part A + Part B violation) and 'vision_review'
+// (fail-closed / ambiguous) are EXCLUDED from everything served to the storefront, but stay
+// in the DB for audit. NULL (unscreened / hand-curated) and 'vision_ok' are served.
+const servedClause = (alias) => `COALESCE(${alias}.settlement_status,'') NOT IN ('vision_blocked','vision_review')`;
+
// ---- search + faceted browse ----
const SORTS = {
newest: 'x.created_at DESC',
@@ -55,7 +62,7 @@ const SORTS = {
price_dn: 'x.price_min DESC NULLS LAST, x.title ASC',
};
function buildWhere(q, params) {
- const w = [];
+ const w = [servedClause('x')]; // storefront never serves settlement-blocked/review designs
if (q.q) { params.push(q.q); w.push(`x.search_tsv @@ plainto_tsquery('english', $${params.length})`); }
for (const [key, col] of [['style','style'],['colorway','colorway'],['motif','motif'],['technique','technique']]) {
if (q[key]) { params.push(q[key]); w.push(`x.${col} = $${params.length}`); }
@@ -118,11 +125,11 @@ app.get('/api/designs', async (req, res) => {
app.get('/api/facets', async (req, res) => {
try {
const facet = async (col) => (await pool.query(
- `SELECT ${col} AS value, count(*)::int AS n FROM designs WHERE ${col} IS NOT NULL GROUP BY ${col} ORDER BY n DESC, value ASC`)).rows;
- const price = (await pool.query('SELECT min(price_min)::float lo, max(price_min)::float hi FROM designs')).rows[0];
+ `SELECT ${col} AS value, count(*)::int AS n FROM designs WHERE ${col} IS NOT NULL AND ${servedClause('designs')} GROUP BY ${col} ORDER BY n DESC, value ASC`)).rows;
+ const price = (await pool.query(`SELECT min(price_min)::float lo, max(price_min)::float hi FROM designs WHERE ${servedClause('designs')}`)).rows[0];
// Collection facet counts — buyer-friendly labels applied in the UI, internal class values here.
const cc = (await pool.query(
- `SELECT content_class AS value, count(*)::int AS n FROM designs WHERE content_class IS NOT NULL GROUP BY content_class`)).rows;
+ `SELECT content_class AS value, count(*)::int AS n FROM designs WHERE content_class IS NOT NULL AND ${servedClause('designs')} GROUP BY content_class`)).rows;
const collection = {
credible: (cc.find(r => r.value === 'credible') || {}).n || 0,
novelty: (cc.find(r => r.value === 'novelty') || {}).n || 0,
@@ -139,7 +146,8 @@ app.get('/api/designs/:id', async (req, res) => {
try {
const r = await pool.query(
`SELECT x.*, d.name AS designer_name, d.slug AS designer_slug, d.country AS designer_country
- FROM designs x JOIN designers d ON d.id=x.designer_id WHERE x.id=$1 OR x.slug=$1`, [req.params.id]);
+ FROM designs x JOIN designers d ON d.id=x.designer_id
+ WHERE (x.id=$1 OR x.slug=$1) AND ${servedClause('x')}`, [req.params.id]);
if (!r.rowCount) return res.status(404).json({ error: 'not found' });
res.json(r.rows[0]);
} catch (e) { console.error(e); res.status(500).json({ error: 'query failed' }); }
@@ -151,7 +159,7 @@ app.get('/api/designers/:slug', async (req, res) => {
if (!d.rowCount) return res.status(404).json({ error: 'not found' });
const items = await pool.query(
`SELECT x.id,x.slug,x.title,x.style,x.colorway,x.dominant_hex,x.img,x.price_min,x.seamless,x.created_at
- FROM designs x WHERE x.designer_id=$1 ORDER BY x.created_at DESC`, [d.rows[0].id]);
+ FROM designs x WHERE x.designer_id=$1 AND ${servedClause('x')} ORDER BY x.created_at DESC`, [d.rows[0].id]);
res.json({ designer: d.rows[0], designs: items.rows, count: items.rowCount });
} catch (e) { console.error(e); res.status(500).json({ error: 'query failed' }); }
});
@@ -184,7 +192,8 @@ app.get('/api/admin/designs', async (req, res) => {
try {
const r = await pool.query(
`SELECT x.id,x.title,x.style,x.colorway,x.motif,x.technique,x.seamless,x.dominant_hex,
- x.img,x.price_min,x.price_max,x.pricing_status,x.created_at,d.name AS designer_name
+ x.img,x.price_min,x.price_max,x.pricing_status,x.created_at,d.name AS designer_name,
+ x.settlement_status,x.settlement_checked_at
FROM designs x JOIN designers d ON d.id=x.designer_id ORDER BY x.created_at DESC`);
res.json({ count: r.rowCount, designs: r.rows });
} catch (e) { console.error(e); res.status(500).json({ error: 'query failed' }); }
← 7136fea Add settlement post-gen vision pass (Gemini 2.5-flash, one c
·
back to Patterndesignlab
·
Add .deploy.conf for prestige.agentabrams.com go-live 3708590 →